SPI clock not uniform?

Status
Not open for further replies.

CollinK

Well-known member
Screenshot from 2016-11-27 20-47-44.jpg

The above is a picture from Saleae Logic of an SPI transaction that happened on a Teensy 3.6 with an Adafruit Bluefruit LE SPI module. Hopefully it shows up how odd the clock signal looks. Instead of being uniform it seems to jump between two different duty cycles. The actual SPI speed kind of seems to stay at 4MHZ.. maybe.... Logic oscillates between telling me it's 3Mhz and 4Mhz. I am capturing at 12MHz which I *think* is fast enough that artifacts shouldn't happen. When I take captures with the same code running on a Due to a BLE module the clock looks perfect. This might not be an issue at all and maybe it's just a testing artifact. But, are there experts out there that would like to comment? It seems like the comm is mostly working fine despite this clock weirdness.
 
discretization error:
At 180mhz, bus clock is 60MHz, so max SPI clock is 30 mhz, and there is not a divisor of 30 that will yield exactly 4mhz, so you get something close. Check the T3.6 reference manual (SPI, chapter 57), you can calculate the actual SPI clock from the various prescalers and bus speeds.
 
Last edited:
Hmm, interesting, yes that might explain a few things. From the sound of it I might be better off with 3MHz for the bus speed since that does divide cleanly into 30MHz. I wonder if it used a divider of 8? That would be 3.75Mhz which might give me the results I saw. Still, my capture was at 12Mhz on the logic probe so it should have been able to show me 3.75Mhz and not cycle back and forth between 3 and 4 MHz like it did. I guess there's no use in wild guessing. I'll RTFM.

Well, thanks for reminding me about clocks and divisors. It should have dawned on me as much as I work with SPI and CAN peripherals but it didn't...
 
FYI - I do see the clock stuff going screwy on the logic analyzer as well. I mostly assume it is due to sampling times. That is why I broke down and purchased a new Logic Pro 8, which hooks up to USB3, such that I can run enough IO pins (MISO/MOSI/SCK/CS/DC/RESET) at something like 100mhz

When doing SPI stuff for ILI9341, I was not getting consistent enough information at 25mhz... So you might try bumping up you sample speed to as high as you can...
 
I hooked up analyzer to T3.6@180mhz with SPI configed for 4MHz. With analyzer at 16mhz, SPI clock pulses are uneven as you note. With analyzer at 40mhz, pulses are cleaner and it reports 3.636Mhz +- 11% or 4.000mhz. The sketch SPI transfers 1000 bytes and reports
2333 us 3.43 mbs 38002003
the last values is CTAR0 reg (hex), which is BR=3, which is divisor of 8, so 30/8 = 3.75 MHz

with analyzer at 50mhz, it reports 3.846mhz +- 9% or 3.571mhz

if you run T3.6@96mhz, then sketch reports
2273 us 3.52 mbs 38011001
so CTAR0 implies (48/4)/3 = 4mhz, which is a nice 4mhz on analyzer as well ;)
 
Last edited:
Thank you for the very thorough analysis. It appears I was getting 3.75Mhz then which is fine with me. The comm was actually working so I wasn't so concerned I just couldn't understand why the clock signal looked so erratic. Now it makes sense. I suppose I should have realized that the logic analyzer might be monitoring at a faster rate than the SPI clock but the two clocks are not synchronized so the timing of measurement falls somewhat randomly within the clock signal.
 
At 180mhz, bus clock is 60MHz, so max SPI clock is 30 mhz, and there is not a divisor of 30 that will yield exactly 4mhz, ...

Actually, it is possible to configure SPI clock for exactly 4mhz, but SPI.cpp won't do it (@Paul, i'll file a github issue). I have a perl script that will give the SPI CTAR0 settings needed for a given bus frequency and desired SPI clock. For SPI of 4mhz with 60 mhz bus, set CTAR0 to 0xb80020002 (that's 120/5/6). Confirmed with test on T3.6 with logic analyzer.

Another example, CPU@96mhz, bus 48mhz wanting SPI of 1mhz, SPI lib uses CTAR of 0x38032003, which is 48/7/8 = 857khz, but you can get exactly 1 mhz with CTAR 0x38012004 48/3/16 = 1mhz.


SPI clock = busHz/PBR * (1+DBR)/BR
 
Last edited:
I was looking up at the SPI pull requests and browsed trough the issues, and saw this one again: https://github.com/PaulStoffregen/SPI/issues/22

Which has to do with with which SPI buss speeds we automatically setup and which one we currently do not... And wondering if we should improve this or not... That is it would be nice to have an exact 4mhz... Makes things like logic analyzers happier ;)


As mentioned the SPI clock is derived from the F_BUSS and depends on several fields in the CTAR:
SPI_CTAR_DBR - Double the speed
SPI_CTAR_PBR(x) - 0-3 which maps to (2, 3, 5, 7)
SPI_CTAR_BR(X) - 0-15 maps to (2, 4, 6, 8, 16, 32, 64, 128, ..., 32768)

Note: as part of this we also setup the Chip Select delay which relies on
SPI_CTR_PBR(x) - above
SPI_CTAR_CSSCK(x) 0-15 maps to (2, 4, 8, 16, 32, 64, ...., 65536) --- Like BR except skips 6

My quick look through the code looks like we do not use the 6 value at all... Example the code checks for /24(3*8) and then checks for /32(2*16) but does not do /30(5*6)...
Should the 30 be added? Would give exact 2MHz in the above cases... But since there is no CSSCK value of 6, we could not choose the chip select delay of exactly one clock. Could either make it larger or smaller...

Now back to 4mhz - as mentioned if F_BUS = 60mhz and we use:
t = SPI_CTAR_PBR(2) | SPI_CTAR_BR(2) | SPI_CTAR_DBR | SPI_CTAR_CSSCK(2);
We have something like:
(60*2)/(5*6) = 4MHz. As you mentioned

But this does have another side effect. That is when you do not use DBR, the SPI clock always has a 50/50 SCK duty cycle. However if DBR is set, the duty cycle depends on PBR and in this case with PBR(2) the duty cycle is: either 40/60 or 60/40 depending on the clock phase (SPI_CTAR_CPHA) would this be a problem?

Another question which came up recently currently the code is setup to only go down to SPI speed /* F_BUS / 768 */, whereas the we can go a whole lot slower...

Side question (need to get back to reading and playing more with c++11...) Wondering with code like we are using here:
Code:
	void init_AlwaysInline(uint32_t clock, uint8_t bitOrder, uint8_t dataMode)
	  __attribute__((__always_inline__)) {
		uint32_t t, c = SPI_CTAR_FMSZ(7);
		if (bitOrder == LSBFIRST) c |= SPI_CTAR_LSBFE;
		if (__builtin_constant_p(clock)) {
			if	  (clock >= F_BUS / 2) {
				t = SPI_CTAR_PBR(0) | SPI_CTAR_BR(0) | SPI_CTAR_DBR
				  | SPI_CTAR_CSSCK(0);
			} else if (clock >= F_BUS / 3) {
				t = SPI_CTAR_PBR(1) | SPI_CTAR_BR(0) | SPI_CTAR_DBR
				  | SPI_CTAR_CSSCK(0);
			} else if (clock >= F_BUS / 4) {
				t = SPI_CTAR_PBR(0) | SPI_CTAR_BR(0) | SPI_CTAR_CSSCK(0);
			} else if (clock >= F_BUS / 5) {
				t = SPI_CTAR_PBR(2) | SPI_CTAR_BR(0) | SPI_CTAR_DBR
				  | SPI_CTAR_CSSCK(0);
			} else if (clock >= F_BUS / 6) {
				t = SPI_CTAR_PBR(1) | SPI_CTAR_BR(0) | SPI_CTAR_CSSCK(0);
			} else if (clock >= F_BUS / 8) {
				t = SPI_CTAR_PBR(0) | SPI_CTAR_BR(1) | SPI_CTAR_CSSCK(1);
			} else if (clock >= F_BUS / 10) {
				t = SPI_CTAR_PBR(2) | SPI_CTAR_BR(0) | SPI_CTAR_CSSCK(0);
			} else if (clock >= F_BUS / 12) {
				t = SPI_CTAR_PBR(1) | SPI_CTAR_BR(1) | SPI_CTAR_CSSCK(1);
			} else if (clock >= F_BUS / 16) {
				t = SPI_CTAR_PBR(0) | SPI_CTAR_BR(3) | SPI_CTAR_CSSCK(2);
			} else if (clock >= F_BUS / 20) {
				t = SPI_CTAR_PBR(2) | SPI_CTAR_BR(1) | SPI_CTAR_CSSCK(0);
			} else if (clock >= F_BUS / 24) {
				t = SPI_CTAR_PBR(1) | SPI_CTAR_BR(3) | SPI_CTAR_CSSCK(2);
			} else if (clock >= F_BUS / 32) {
				t = SPI_CTAR_PBR(0) | SPI_CTAR_BR(4) | SPI_CTAR_CSSCK(3);
			} else if (clock >= F_BUS / 40) {
				t = SPI_CTAR_PBR(2) | SPI_CTAR_BR(3) | SPI_CTAR_CSSCK(2);
			} else if (clock >= F_BUS / 56) {
				t = SPI_CTAR_PBR(3) | SPI_CTAR_BR(3) | SPI_CTAR_CSSCK(2);
			} else if (clock >= F_BUS / 64) {
				t = SPI_CTAR_PBR(0) | SPI_CTAR_BR(5) | SPI_CTAR_CSSCK(4);
			} else if (clock >= F_BUS / 96) {
				t = SPI_CTAR_PBR(1) | SPI_CTAR_BR(5) | SPI_CTAR_CSSCK(4);
			} else if (clock >= F_BUS / 128) {
				t = SPI_CTAR_PBR(0) | SPI_CTAR_BR(6) | SPI_CTAR_CSSCK(5);
			} else if (clock >= F_BUS / 192) {
				t = SPI_CTAR_PBR(1) | SPI_CTAR_BR(6) | SPI_CTAR_CSSCK(5);
			} else if (clock >= F_BUS / 256) {
				t = SPI_CTAR_PBR(0) | SPI_CTAR_BR(7) | SPI_CTAR_CSSCK(6);
			} else if (clock >= F_BUS / 384) {
				t = SPI_CTAR_PBR(1) | SPI_CTAR_BR(7) | SPI_CTAR_CSSCK(6);
			} else if (clock >= F_BUS / 512) {
				t = SPI_CTAR_PBR(0) | SPI_CTAR_BR(8) | SPI_CTAR_CSSCK(7);
			} else if (clock >= F_BUS / 640) {
				t = SPI_CTAR_PBR(2) | SPI_CTAR_BR(7) | SPI_CTAR_CSSCK(6);
			} else {	 /* F_BUS / 768 */
				t = SPI_CTAR_PBR(1) | SPI_CTAR_BR(8) | SPI_CTAR_CSSCK(7);
			}
		} else {
			for (uint32_t i=0; i<23; i++) {
				t = ctar_clock_table[i];
				if (clock >= F_BUS / ctar_div_table[i]) break;
			}
		}
Where we have this big set of ifs that we use if the clock value passed in is a constant, else we look up in table... Wondering if this can somehow be all condensed by using something like constexpr. Again still learning about this new fangled stuff :D

Thoughts?
 
Quick update: not sure if this will go anywhere, but did do a little playing with the above.

Example with default speed of T3.6 as expected was able to get a nice 4MHZ SPI output, as you can see in the Logic Analyzer output.

screenshot.jpg

In the measurements as expected we don't have a 50/50 duty cycle of the clock. The high part of the pulse measures as .1us and the low part as .15us...

Also with a little playing around with constexpr and the like so far not having much luck of having the SPI Settings with constant value speed to use the table driven code and have it reduce down to simple set like I believe this big if/else if... code should do... Will play a little more, but probably not important...

Edit: Note to self (and others)
Not sure how many of the possible variations that make sense to add to the tables. Yes it is nice to add the entry mentioned and used above to get a proper
4mhz SPI clock signal at the default Clock speeds for the T3.6 (180) and T3.5 (120) who both have T_BUS of 60MHz, however with both of them,
You can run at a slightly higher CPU speed T3.6 (192) and T3.5 (144) which then has T_BUS = 48MHz Which then allows us to to get a actual 4Mhz SPI clock (PBR = 1(3) and BR=1(4)) and likewise 8MHZ( 48/(3*2))
 
Last edited:
Status
Not open for further replies.
Back
Top