Forum Rule: Always post complete source code & details to reproduce any issue!

# Thread: Reset audio board codec SGTL5000 in realtime processing

1. There are multiple sources for the frac synth in Teensy - F_CPU, F_PLL etc. For 180MHz it is F_CPU (I think), so the default Mclk = 180MHz * (16 / 255) = ~11.29MHz, and the SR is ~11.29MHz / 256 = ~44.1ksps.

Let's say you want ~92ksps, then Mclk = 92ksps*256 = ~23.5MHz, so you need a synth ratio close to 23.5/180, a good candidate would be 2/15 (MULT=2, DIV=15). So the exact SR will be 180MHz * (2 / 15) / 256 = 93.75ksps. It is the nominator (MULT), not the denominator that needs to be 1 or 2.

I will be traveling for the next few days, should be back on Monday if you need more help.  Reply With Quote

2. Frank,
the numbers should only to be small, avoiding too high intermediate frequencies, I believe also an even DIV would be better than an odd one, but I have no proof.

for SGTL500 you get 192 kHz you would need a MCLK of 49.152 MHz (192000*256)
using F_CPU=240 MHz you would need MULT = 128 and DIV 625 but that is unrealistic

now if you divide all by 18 and use MULT=8 and DIV=39 you get a MCLK of 49.2308 MHz and a sampling frequency of 192.3 kHz
As DIV is an odd number let us try an odd MULT, say 7 then with DIC=34 you get a sampling frequency of 193.0147 kHz

For 180 MHz the required scaling of 3.621 is easily achieved but rounding down to 3.62 one gets MULT = 50 and DIV=181 for a sampling frequency of 194.2 kHz
dividing all by 10 one gets for a MULT=5 and DIV = 18 a sampling frequency of 195.3215 kHz
etc. etc.
a lot of possibilities to play.

Edit: I see vladn has some other rules, but I agree the MULT should be as small as possible. After all the CPU has to generate the intermediate frequency that should not be too high  Reply With Quote

3. Originally Posted by WMXZ I see vladn has some other rules, but I agree the MULT should be as small as possible.
It is not my personal rule, it is in the K6x reference manual - only 1 and 2 in the MULT generate low jitter Mclk. In case MULT=2 the divider simply uses both edges of the reference clock. Any MULT > 2 requires true fractional synthesis. Just look at the Mclk on any decent scope (I did).  Reply With Quote

4. This is the code (slightly modified) that generated the table for https://forum.pjrc.com/threads/38753...he-sample-rate

(Dunno if it's useful here, but I think I have not published it so far, so anyway... maybe it can help to find better values by modfiying it ( as a starting point) )

Code:
```/*
(c) Frank Bösing
Sketch to calculate I2S FRACT / DIVIDE values

*/

/* Quote from manual:

The MCLK fractional clock divider uses both clock edges from the input clock to
generate a divided down clock that will approximate the output frequency, but without
creating any new clock edges. Configuring FRACT and DIVIDE to the same value will
result in a divide by 1 clock, while configuring FRACT higher than DIVIDE is not
supported. The duty cycle can range from 66/33 when FRACT is set to one less than
DIVIDE down to 50/50 for integer divide ratios, and will approach 50/50 for large noninteger
divide ratios. There is no cycle to cycle jitter or duty cycle variance when the
divide ratio is an integer or half integer, otherwise the divider output will oscillate
between the two divided frequencies that are the closest integer or half integer divisors of
the divider input clock frequency. The maximum jitter is therefore equal to half the
divider input clock period, since both edges of the input clock are used in generating the
divided clock.
*/

const float pllfreqs[] = {16000000, 72000000, 96000000, 120000000, 144000000, 168000000, 180000000, 192000000, 216000000, 240000000};
const float samplefreqs[] = { 8000, 11025, 16000, 22050, 32000, 44100, 44117.64706 , 48000, 88200, 44117.64706 * 2, 96000, 176400, 44117.64706 * 4, 192000/*, 200000, 210000, 220000, 230000, 240000, 250000, 260000, 270000, 280000, 290000, 300000*/};
const int numpll = sizeof(pllfreqs) / sizeof(float);
const int numfreqs = sizeof(samplefreqs) / sizeof(float);

void calc(const float pll, const float freq) {
float p = pll / 256.0;
float err = 1e6;
int mu = 0;
int di = 0;
for (int m = 1; m < 256 ; m++) {
for (int d = m; d < 4096; d++) {
float f = (p * m) / d;
float e = fabs(freq - f);
if (e < err) {
mu = m;
di = d;
err = e;
}
}
}
#if 1
Serial.printf("{%d, %d}", mu, di);
#else
Serial.printf("{%d, %d /* %.1fHz */}", mu, di, (pll * mu / di) / 256.0);
#endif

}

void setup() {
delay(1000);

Serial.printf("const int numfreqs = %d;\n", numfreqs);

Serial.print("const int samplefreqs[numfreqs] = {");
for (int p = 0; p < numfreqs; p++)
Serial.printf("%.0f%s", samplefreqs[p], (p <  numfreqs - 1) ? ", " : " " );
Serial.println("};");

Serial.println();

for (int p = 0; p < numpll; p++) {
Serial.printf("#%s (F_PLL==%.0f)\n", p == 0 ? "if" : "elif", pllfreqs[p] );
Serial.printf(" const tmclk clkArr[numfreqs] = {");
for (int s = 0; s < numfreqs; s++) {
calc(pllfreqs[p], samplefreqs[s]);
if (s < numfreqs - 1) Serial.print(", ");
}
Serial.println("};");
}
Serial.println("#endif");
}

void loop() {}```
Edit: Attention: bugfix in code

It tries to generate the desired frequency as close as possible. Perhaps there is a better way to do this?

The manual says:
The MCLK fractional clock divider uses both clock edges from the input clock to
generate a divided down clock that will approximate the output frequency, but without
creating any new clock edges. Configuring FRACT and DIVIDE to the same value will
result in a divide by 1 clock, while configuring FRACT higher than DIVIDE is not
supported. The duty cycle can range from 66/33 when FRACT is set to one less than
DIVIDE down to 50/50 for integer divide ratios, and will approach 50/50 for large noninteger
divide ratios. There is no cycle to cycle jitter or duty cycle variance when the
divide ratio is an integer or half integer, otherwise the divider output will oscillate
between the two divided frequencies that are the closest integer or half integer divisors of
the divider input clock frequency. The maximum jitter is therefore equal to half the
divider input clock period, since both edges of the input clock are used in generating the
divided clock.  Reply With Quote

5. "there is no cycle to cycle jitter or duty cycle variance when the
divide ratio is an integer or half integer, "

- maybe it's possible to modify the program above to find jitter-free Samplerates?  Reply With Quote

6. So, jitter-free freqs with 50%50 duty cycle are for example 175781Hz, 140625, 117188, 100446, 46875 Hz.
Can someone confirm this ? I'm not sure if my calculation is right.  Reply With Quote

7. Frank: here is the formula from the K66 datasheet (its in a table, but this is the gist of it):
**********
MCLK Divide
Sets the MCLK divide ratio such that: MCLK output = MCLK input * ( (FRACT + 1) / (DIVIDE + 1) ).
FRACT must be set equal or less than the value in the DIVIDE field.
NOTE:
When using fractional divide values, the MCLK duty cycle will not always be 50/50.
*******
What Vladn called MULT, Freescale labels FRACT+1. Note the "+1"s.
MCLK input, in your case is 180 MHZ. Vladn has found that FRACT +1 needs to be 1 or two. Freescale mentions that they can use both edges of the input clock (i.e. basically doubling the clock frequency) , so that explains how you can use the "2" for FRACT+1. There is no PLL in this circuit, so you are not actually multiplying the 180 MHZ clock by what Vladn refers to as MULT. They are just using a fractional divider circuit (sometimes called a pulse swallowing divider) to simulate what would be an integer frequency multiplication followed by a subsequent integer division. This fractional divider will always have some jitter when it is not dividing by an integer value.
That said, if you take the formula you derived: SR = 703125 * MULT /DIV
and use 1 or 2 for MULT, you can find suitable DIV values easily to get SRs close to the ones you now use. i.e.
703125 *1/7 = 100.466 KHz
703125 *1/14 = 50.223 KHz
703125 *2/6 = 234.375 KHz
The "output_i2s.cpp" code takes into account the "+1"s stated in the Freescale formula which I noted above, so you can set the MULT and DIV values in output_i2s.cpp to the values of the 3 examples above.
I don't know if this explains things better- I am basically agreeing with vladn's observations, but trying to explain them using Freescale's documentation and knowing something about the clock generation circuitry that I believe they are using in the K66.(Freescale is sketchy on details)
PS- I wrote this before seeing your current post.  Reply With Quote

8. "There is no cycle to cycle jitter or duty cycle variance when the divide ratio is an integer or half integer, otherwise..." If you reduce the fraction by the GCD that precisely means that MULT could be either 1 or 2.

"703125 *2/6 = 234.375 KHz" - it makes sense to use irreducible fractions, 1 / 3 is better For example to get Mclk ~4096k (~16ksps) for my AF I/Q processing I use 2 / 47 ratio on a 96MHz system PLL clock reference (which covers F_CPU of 24, 48 and 96MHz).  Reply With Quote

9. Hi Vlad, Walter, Frank, Brian,

thanks a lot to you all for the excellent information and also your code, Frank B!

I was confused by the nominator/denominator english terms . . . So now that is clear, thanks Vlad! And that hint on "+1" is also very helpful, Brian!

Now it is time for me to carefully digest all that; an excellent opportunity with that longer weekend here in Central Europe ;-). I will be travelling, so time to experiment with real hardware will be the weekend after.

I will report back with results then, however I am not sure whether I will be able to really measure the jitter. But I will have a close look on spurs in my receive spectrum, once I have incorporated all the info you gave me into the Teensy Convolution SDR.

Thanks a lot again!

All the best,

Frank DD4WH  Reply With Quote

10. Anyone has some figures or reference on the amount of jitter noise for fractional dividers?  Reply With Quote

11. Sorry, can't help on this without digging into the math textbooks. There probably is a jitter calc somewhere for simple fractional dividers. But if I can clearly see the Mclk edge jitter on the scope it makes me uncomfortable.

SiLabs uses more sophisticated fractional divider HW with some form of edge correction and their reference PLLs runs at much higher frequencies, so the jitter is 1..3 orders of magnitude smaller. For example Si5338 and Si514 reference is ~2.5GHz. They have even faster ICs, but they are not suitable for a battery powered project like mine. But even those can not compare to the properly designed crystal and analog LC oscillators (ignoring the heavy frequency drift of the latter).

Also we should not forget that SDR community is a minority here. For some audio apps having a standard sampling rate is a must. For other audio apps (like synth) it is not that critical. My minor suggestion to Paul is to make AUDIO_SAMPLE_RATE_EXACT a class variable, rather than a #define constant. Then we can all be happy .  Reply With Quote

12. Originally Posted by vladn Sorry, can't help on this without digging into the math textbooks. There probably is a jitter calc somewhere for simple fractional dividers. But if I can clearly see the Mclk edge jitter on the scope it makes me uncomfortable.
What I was looking for is some graphs or formula that relate % of MCLK jitter to dB spectral noise level increase. I say spectral, as I would assume that spectral noise level are nor equally impacted by MCLK jitter.

Edit:
OK, I can myself use Google, so I found  Reply With Quote