Go Down

### Topic: Timing question (Read 2047 times)previous topic - next topic

#### vpapanik

##### Jul 12, 2012, 12:15 am
Hello !

I have two different codes that read a 512 byte buffer from an SD card using SPI. The first one is mine and the second is taken from sdfatlib. They appear to be equivalent, however :

in the first one, the pause between SPI reads is 2.75 ?s :

Code: [Select]
`for (uint16_t i = 0; i < 512; i++) {    SPDR = 0xFF;    while (!(SPSR & (1 << SPIF)));    buffer[i] = SPDR;}`

and in the second one, time drops to 1.75 ?s, which means 8 less clock cycles on my Arduino Pro 8 MHz. That is some difference !

Code: [Select]
`SPDR = 0xFF;for (uint16_t i = 0; i < 511; i++) {        while (!(SPSR & (1 << SPIF)));    buffer[i] = SPDR;    SPDR = 0xFF;}// wait for last bytewhile (!(SPSR & (1 << SPIF)));buffer[511] = SPDR;`

Still, I can not explain the above difference, so I would really appreciate any help

#### nickgammon

#1
##### Jul 12, 2012, 01:00 amLast Edit: Jul 12, 2012, 09:02 am by Nick Gammon Reason: 1
It's a good question, and I can reproduce your results. I checked the generated machine code and it contains the same instructions (albeit in a different order). So, we ask ourselves, "how come the same instructions take longer in one case than the other?".

I timed 6 clock cycles difference.

Code: [Select]
`    SPDR = 0xFF;   // <---- send    while (!(SPSR & (1 << SPIF)));  // <---- immediately wait for buffer empty`

Since the default SPI transfer rate is 1/4 of the system clock, we expect it to take 4 clock cycles to send that byte. So the while loop will wait for at least 4 cycles.

(edit) See below: At least 32 cycles (8 x 4).

Compare to:

Code: [Select]
`for (uint16_t i = 0; i < 511; i++) {        while (!(SPSR & (1 << SPIF)));    buffer[i] = SPDR;    SPDR = 0xFF;}`

Extra work is done here after setting SPDR, namely:

Code: [Select]
` 11e: 82 e0       ldi r24, 0x02 ; 2 120: ef 3f       cpi r30, 0xFF ; 255 122: f8 07       cpc r31, r24 124: b1 f7       brne .-20     ; 0x112 <loop+0x12>`

In other words, the "end of loop" test. This is 5 cycles here. So the 5 cycles are being used during the SPI transfer. So when we go back to the start of the loop the transfer is over, basically.

This accounts for 5 cycles, not 6, but the difference is probably that the loop here:

Code: [Select]
`    while (!(SPSR & (1 << SPIF)));`

... would take at least 4 cycles, so the SPI transfer probably finishes half-way through that test.
Please post technical questions on the forum, not by personal message. Thanks!

#### nickgammon

#2
##### Jul 12, 2012, 01:23 am
My explanation above is a little incorrect. The SPI clock frequency is (by default) 1/4 of the system clock, but that is per bit.

So an SPI transfer will actually take 32 (4 x 8) clock cycles, not just 4.

However my conclusion stands, that of those 32 cycles, we have used up 5 in the "end of for loop" test, thereby accounting for a reduction of 5 cycles.

The 6th cycle would be accounted for by the fact that the test for the SPIF flag takes 4 cycles, so the test must conclude on a boundary of 4 cycles (so we won't save exactly 5 cycles).
Please post technical questions on the forum, not by personal message. Thanks!

#### vpapanik

#3
##### Jul 12, 2012, 06:32 am
Dear Nick, your explanation makes perfect sense and I thank you very much indeed

#### vpapanik

#4
##### Jul 12, 2012, 07:13 am
just noting that I've set the SPI frequency to the highest possible, i.e 1/2 system frequency. However, the difference lies only on the fact that the while command takes advantage of the for-loop overhead.

Thanks a lot again !

Go Up