Thursday, May 8, 2014

Si570 math using integer math

So, if I have learned nothing else, it is that I am no mathematician and am really slow at figuring out this stuff.  Here is where I am at with regard to using integer math instead of floating point math for calculating the Si570 frequency setting elements.

Consider the following code segment:

uint32_t fxtal;
uint32_t fOut = 56320000; // My Si570 reset frequency
uint64_t RFREQ = 0x2B6109f08; // My Si570 RFREQ after reset
uint16_t HSDiv = 11; // My Si570 HSDiv after reset
uint16_t N1 = 8; // My Si570 N1 after reset
uint64_t tmp;

// Calculate fXtal for my device
fxtal = (((uint64_t) fOut * HSDiv * N1) << 28) / RFREQ;

// Calculate fOut value
tmp = ((uint64_t) fxtal * RFREQ / (uint64_t) (HSDiv * N1));
tmp = tmp + ((tmp & 1<<(28-1))<<1); // Round result
fOut = tmp >> 28;

Ok, so that is a bunch of math weirdness...  Let me try to illustrate a bit what I am doing.

The fxtal and fout values are 32 bit integers.  RFREQ on the other hand is a 38 bit fractional value with 10 bits of integer and 28 bits of fraction stored in a 64 bit unsigned integer.

So, promoting fOut to 64 bits before multiplication and then shifting left by 28 bits will align the decimal point between this partial result and the implied decimal point in RFREQ.  The division by RFREQ results in a 32 bit value with zero fractional bits.  In the case of the code above, I get the value 114252365 Hz.  I should probably round this result, but let's leave it as is for the moment.

Now that I know the crystal frequency, I should be able reverse the calculation to obtain the same fOut that I started with.  Remember that fOut = fxtal * RFREQ / (HSDiv * N1).

fOut is a 32 bit unsigned integer with zero fractional bits.  The formula contains an element (RFREQ) that has 28 fractional bits and 10 integer bits stored in a 64 bit integer type.  Basically it is the number 11644477192 / 2^28 which is 43.3790579140186309814453125 out to more fractional digits than I can handle (or need).

So, to handle this multiplication I use the integer value representation of RFREQ as a fixed point number with an implied decimal point between the 28th and 29th bits.  Multiplying RFREQ by fxtal which is a 32 bit integer, gives me a result that is 42 bits of integer (32+10) and 28 bits of fraction (28+0).  The result size (in bits) is the sum of the number of bits in the fractional part of each number plus the sum of the number of bits in the integer part.  Even though the result is 70 bits total (42+28), the upper 6 bits are unused, so the truncation to fit in a 64 bit unsigned integer results in no data loss.

Rounding the result is recommended, so we isolate at the most significant fractional bit and add it to the integer part.  Now, we have fOut scaled by 2^28.  Shifting right by 28 bits gives us the integer result we started with of 56320000.

Ok, now to calculate RFREQ, HSDiv and N1 given a desired fOut in the next posting.

More to come...