Forum Rule: Always post complete source code & details to reproduce any issue!
Results 1 to 6 of 6

Thread: Different SPI Behavior on Teensy 4.0 and 4.1

  1. #1

    Different SPI Behavior on Teensy 4.0 and 4.1

    Hello,

    I'm running into a wall with a strange SPI glitch.

    Teesyduino Version: 1.53
    Arduino IDE Version: 1.8.12

    Observed Behaviors:
    SPI.transfer(); appears to block on Teensy 4.0, as expected.
    SPI.transfer(); does not appear to block on Teensy 4.1.
    Uncommenting delays shown below causes crazy performance on 4.1 (clocking out 32+ bits of data), but behaves as expected on Teensy 4.0.
    Code has been executed with and without the .begin() call before starting a transaction. it does not change the observed behavior.
    I sometimes observe the MISO data received in call #1 being immediately returned on the second call of SPI.transfer.

    Code:
    SPI.begin();
        digitalWrite(self->ADC_CS, HIGH);
        SPI.beginTransaction(SPISettings(ADCEXT_SPI_CLK, MSBFIRST, SPI_MODE0)); //Fire up SPI interface, defined in adcext.h (14Max)
        digitalWrite(self->ADC_CS, LOW);
        //delayMicroseconds(2);
        upperdata = SPI.transfer((uint8_t) 0x00); //Send 0 while reading a byte
        //delayMicroseconds(10);
        lowerdata = SPI.transfer((uint8_t) 0x00); //Send 0 while reading a byte
        //delayMicroseconds(10);
        digitalWrite(self->ADC_CS, HIGH);
        SPI.endTransaction();
    SPI.end();

    Scope capture on Teensy 4.0:
    Click image for larger version. 

Name:	TEENSY4_0_capture.png 
Views:	13 
Size:	48.4 KB 
ID:	21011

    Scope capture on Teensy 4.1:
    Click image for larger version. 

Name:	TEENSY4_1_capture.png 
Views:	14 
Size:	49.1 KB 
ID:	21012

    So... this appears to be a blocking issue with the DMA-offloading of SPI transactions on Teensy 4.1.
    I did some searching, but can't seem to find anything relating to SPI Issues on Teensy 4.1.

    SPI.transfer16() appears to behave the same.
    SPI.transfer(rxbuf* txbuf* numbytes) crashes on the second call. I suspect a memory access issue (attempting to write to a now obsolete pointer, because the function did not effectively block)
    DMA is used for SPI in both 4.0 and 4.1 Teensy... since they are the same micro-controller, leaving me rather confused.

    Thank you in Advance for those who have crossed this behavior before!

  2. #2
    Senior Member+ KurtE's Avatar
    Join Date
    Jan 2014
    Posts
    7,415
    Interesting - Not Sure what is going on here.

    As Same code and same processor?
    The code for simple transfer is:
    Code:
    	// Write to the SPI bus (MOSI pin) and also receive (MISO pin)
    	uint8_t transfer(uint8_t data) {
    		// TODO: check for space in fifo?
    		port().TDR = data;
    		while (1) {
    			uint32_t fifo = (port().FSR >> 16) & 0x1F;
    			if (fifo > 0) return port().RDR;
    		}
    		//port().SR = SPI_SR_TCF;
    		//port().PUSHR = data;
    		//while (!(port().SR & SPI_SR_TCF)) ; // wait
    		//return port().POPR;
    	}
    Transfer16 is more ore less identical except it switches to and from 16 bit mode:
    Code:
    	uint16_t transfer16(uint16_t data) {
    		uint32_t tcr = port().TCR;
    		port().TCR = (tcr & 0xfffff000) | LPSPI_TCR_FRAMESZ(15);  // turn on 16 bit mode 
    		port().TDR = data;		// output 16 bit data.
    		while ((port().RSR & LPSPI_RSR_RXEMPTY)) ;	// wait while the RSR fifo is empty...
    		port().TCR = tcr;	// restore back
    		return port().RDR;
    	}
    Note: the version of code that sends a buffer:
    Code:
    void SPIClass::transfer(const void * buf, void * retbuf, size_t count)
    {
    
    	if (count == 0) return;
        uint8_t *p_write = (uint8_t*)buf;
        uint8_t *p_read = (uint8_t*)retbuf;
        size_t count_read = count;
    
    	// Pass 1 keep it simple and don't try packing 8 bits into 16 yet..
    	// Lets clear the reader queue
    	port().CR = LPSPI_CR_RRF | LPSPI_CR_MEN;	// clear the queue and make sure still enabled. 
    
    	while (count > 0) {
    		// Push out the next byte; 
    		port().TDR = p_write? *p_write++ : _transferWriteFill;
    		count--; // how many bytes left to output.
    		// Make sure queue is not full before pushing next byte out
    		do {
    			if ((port().RSR & LPSPI_RSR_RXEMPTY) == 0)  {
    				uint8_t b = port().RDR;  // Read any pending RX bytes in
    				if (p_read) *p_read++ = b; 
    				count_read--;
    			}
    		} while ((port().SR & LPSPI_SR_TDF) == 0) ;
    
    	}
    
    	// now lets wait for all of the read bytes to be returned...
    	while (count_read) {
    		if ((port().RSR & LPSPI_RSR_RXEMPTY) == 0)  {
    			uint8_t b = port().RDR;  // Read any pending RX bytes in
    			if (p_read) *p_read++ = b; 
    			count_read--;
    		}
    	}
    }
    Again simply loops putting stuff out on the Transfer fifo and wait for that many bytes to come back on the RX fifo before it returns.

    In all of the cases above there is no DMA involved.

    The only case that uses DMA is if use the version of transfer with an EventResponder:
    Code:
    bool SPIClass::transfer(const void *buf, void *retbuf, size_t count, EventResponderRef event_responder)
    The only differences in this SPI code between T4 and T4.1, is which IO pins can be used for certain functions. Like SPI1 having 2 possibilities for MISO and hardware CS pins.

    So I have no idea what is different between your two setups? Which version of Arduino IDE, Teensyduino?

    Would help to see full example sketch that shows this? Like you said a crash?

    Kurt

  3. #3
    Senior Member+ Frank B's Avatar
    Join Date
    Apr 2014
    Location
    Germany NRW
    Posts
    6,920
    As Kurt said - the Code is identical.

    It would be good to see a complete program that we can copy&paste to Arduin( "Forum Rule: Always post complete source code & details to reproduce any issue! " (above)). Without that, Kurt already said all, and we can't help more.

  4. #4
    Fair points, Kurt & Frank. The true source is a mess at the moment. many files stitched together.

    I'll try to clean this up and get it loaded on Git tonight.
    I attempted to duplicate this issue out of circuit, no dice.

    I think I heard something about clock reflections causing issues with this IC, though I can't find the PJRC forum post at the moment.

    The fact this code runs differently in / out of circuit is strange. I'll attempt adding a small series resistor near the clock output of this IC, and see if that fixes the behavior.

  5. #5
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    22,467
    Quote Originally Posted by eeforeveryone View Post
    I'll try to clean this up and get it loaded on Git tonight.
    Rather than trying to clean up a large project, maybe try writing just the smallest possible program which can show the problem. If there really is something different between Teensy 4.0 and 4.1 which needs fixing (hard as that is the imagine), a small & simple program to demonstrate the problem will greatly improve the odds that we can reproduce the issue and ultimately fix it.

  6. #6
    Hello all,

    Thank you for the responsiveness and support! At this point, I'm very confident that it is not a software or library issue.

    To update this, I did two tests:

    1. Run the original code, on a teensy out of circuit
    2. Run the original code on a teensy in circuit, with a series 100ohm resistor electrically near the teensy on the clock pin.

    Both of these tests performed as expected (same performance on teensy 4.0 and 4.1, function blocked as expected), so it appears that clock reflections were causing glitches in the SPI hardware!

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •