Skip to content

Commit 06d609d

Browse files
Refactor misaligned transfer, avoid RMW to FIFO
The SPI FIFO can't properly do RMW (i.e. bytewise updates) because when you read the FIFO you are actually reading the SPI read data, not what was written into the write FIFO. Refactor the transferBytes to take account of this. For aligned input and outputs, perform as before (but handle non-x4 sizes properly). For misaligned inputs, if it's unidirectional then do bytewise until the direction data pointer is aligned and then do 32b accesses. Fod bidirectional and misaligned inputs, copy the output data to an aligned buffer, do the transfer, then copy the read back data from temp aligned buffer to the real input buffer.
1 parent b2f745b commit 06d609d

File tree

2 files changed

+68
-65
lines changed

2 files changed

+68
-65
lines changed

Diff for: libraries/SPI/SPI.cpp

+67-65
Original file line numberDiff line numberDiff line change
@@ -528,94 +528,96 @@ void SPIClass::transferBytes(const uint8_t * out, uint8_t * in, uint32_t size) {
528528
}
529529

530530
/**
531+
* Note:
532+
* in and out need to be aligned to 32Bit
533+
* or you get an Fatal exception (9)
531534
* @param out uint8_t *
532535
* @param in uint8_t *
533536
* @param size uint8_t (max 64)
534537
*/
535-
void SPIClass::transferBytes_(const uint8_t * out, uint8_t * in, uint8_t size) {
536-
while(SPI1CMD & SPIBUSY) {} // Make sure we're IDLE on the SPI
537-
538-
int bytesToTransfer = size; // How much to read/write from the FIFO in bytes
539538

540-
if (out && !in) {
541-
int bytesLeft = size;
542-
543-
// Only transmitting, get out 32b aligned
544-
while (bytesLeft && ((uint32_t)out & 3)) {
545-
SPI.transfer(*(out++));
546-
bytesLeft--;
547-
}
539+
void SPIClass::transferBytesAligned_(const uint8_t * out, uint8_t * in, uint8_t size) {
540+
if (!size)
541+
return;
548542

549-
if (!bytesLeft) return;
550-
bytesToTransfer = bytesLeft;
543+
while(SPI1CMD & SPIBUSY) {}
544+
// Set in/out Bits to transfer
551545

552-
// Now do 32b writes until we have 0 or fewer bytes left
553-
// Note we can read and store past the end of string because it will not be sent (setDataBits)
554-
uint32_t *dataPtr = (uint32_t*)out;
555-
volatile uint32_t *fifoPtr = &SPI1W0;
556-
while (bytesLeft > 0) {
557-
*(fifoPtr++) = *(dataPtr++);
558-
bytesLeft -= 4;
559-
}
560-
// Remainder will be sent out of loaded FIFO below
561-
} else if (in && !out) {
562-
int bytesLeft = size;
563-
564-
// Only receiving, get in 32b aligned
565-
while (bytesLeft && ((uint32_t)in & 3)) {
566-
*(in++) = SPI.transfer(0xff);
567-
bytesLeft--;
568-
}
546+
setDataBits(size * 8);
569547

570-
if (!bytesLeft) return;
571-
bytesToTransfer = bytesLeft;
548+
volatile uint32_t *fifoPtr = &SPI1W0;
549+
uint8_t dataSize = ((size + 3) / 4);
572550

573-
// Now send 32b writes of all 0xff, actual read will be done below
574-
// Note we can write add'l 0xffs and they won't be sent thanks to setDataBits below
575-
volatile uint32_t *fifoPtr = &SPI1W0;
576-
while (bytesLeft > 0) {
577-
*(fifoPtr++) = 0xffffffff;
578-
bytesLeft -= 4;
551+
if (out) {
552+
uint32_t *dataPtr = (uint32_t*) out;
553+
while (dataSize--) {
554+
*fifoPtr = *dataPtr;
555+
dataPtr++;
556+
fifoPtr++;
579557
}
580-
} else if (in && out && (((uint32_t)in & 3) || ((uint32_t)out & 3))) {
581-
// Bidirectional and we have one or both pointers misaligned
582-
while (size) {
583-
*(in++) = SPI.transfer(*(out++));
584-
size--;
558+
} else {
559+
// no out data only read fill with dummy data!
560+
while (dataSize--) {
561+
*fifoPtr = 0xFFFFFFFF;
562+
fifoPtr++;
585563
}
586-
// All the data was transferred byte-wise, we're done
587-
return;
588564
}
589565

590-
// At this point we've got aligned (or null) in and out and the FIFO is filled
591-
// with whatever data we're going to transmit as aligned
592-
593-
// Set in/out bits to transfer
594-
while (SPI1CMD & SPIBUSY) {} // Paranoia, make sure SPI is idle
595-
setDataBits(bytesToTransfer * 8);
596-
597-
// Start the transfer and wait for done
598566
SPI1CMD |= SPIBUSY;
599-
while (SPI1CMD & SPIBUSY) { /* noop, waiting */ }
567+
while(SPI1CMD & SPIBUSY) {}
600568

601569
if (in) {
602-
// Bulk read out by 4 until we have 0..3 left
603-
uint32_t *dataPtr = (uint32_t*)in;
604-
volatile uint32_t *fifoPtr = &SPI1W0;
605-
while (bytesToTransfer >= 4) {
570+
uint32_t *dataPtr = (uint32_t*) in;
571+
fifoPtr = &SPI1W0;
572+
dataSize = size;
573+
while (dataSize >= 4) {
606574
*(dataPtr++) = *(fifoPtr++);
607-
bytesToTransfer -= 4;
608-
in += 4; // Keep track of the in ptr for any stragglers
575+
dataSize -= 4;
576+
in += 4;
609577
}
610-
611-
// Bytewise read out the remainder
612-
volatile uint8_t *fifoPtrB = (uint8_t*)fifoPtr;
613-
while (bytesToTransfer--) {
578+
volatile uint8_t *fifoPtrB = (volatile uint8_t *)fifoPtr;
579+
while (dataSize--) {
614580
*(in++) = *(fifoPtrB++);
615581
}
616582
}
617583
}
618584

585+
586+
void SPIClass::transferBytes_(const uint8_t * out, uint8_t * in, uint8_t size) {
587+
if (!((uint32_t)out & 3) && !((uint32_t)in & 3)) {
588+
// Input and output are both 32b aligned or NULL
589+
transferBytesAligned_(out, in, size);
590+
} else if (!out && ((uint32_t)in & 3)) {
591+
// Input only and misaligned, do bytewise until in aligned
592+
while (size && ((uint32_t)in & 3)) {
593+
*(in++) = transfer(0xff);
594+
size--;
595+
}
596+
transferBytesAligned_(out, in, size);
597+
} else if (!in && ((uint32_t)out & 3)) {
598+
// Output only and misaligned, bytewise xmit until aligned
599+
while (size && ((uint32_t)out & 3)) {
600+
transfer(*(out++));
601+
size--;
602+
}
603+
transferBytesAligned_(out, in, size);
604+
} else {
605+
// HW FIFO has 64b limit, so just align in RAM and then send to FIFO aligned
606+
uint8_t outAligned[64]; // Stack vars will be 32b aligned
607+
uint8_t inAligned[64]; // Stack vars will be 32b aligned
608+
if (out) {
609+
memcpy(outAligned, out, size);
610+
} else {
611+
memset(outAligned, 0xff, size); // 0xff = no xmit data
612+
}
613+
transferBytesAligned_(outAligned, inAligned, size);
614+
if (in) {
615+
memcpy(in, inAligned, size);
616+
}
617+
}
618+
}
619+
620+
619621
#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_SPI)
620622
SPIClass SPI;
621623
#endif

Diff for: libraries/SPI/SPI.h

+1
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ class SPIClass {
7979
uint8_t pinSet;
8080
void writeBytes_(const uint8_t * data, uint8_t size);
8181
void transferBytes_(const uint8_t * out, uint8_t * in, uint8_t size);
82+
void transferBytesAligned_(const uint8_t * out, uint8_t * in, uint8_t size);
8283
inline void setDataBits(uint16_t bits);
8384
};
8485

0 commit comments

Comments
 (0)