This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

CCS/TMS320F28335: Sporadic data misalignment using multiframe McBSP and DMA

Part Number: TMS320F28335


Tool/software: Code Composer Studio

Hi, I am using the McBSP feature of the Delfino to generate a SPI style waveform for interfacing with a sensor.  The sensor requires a long stream of clocks to read in data serially over a MISO data pin.

I am achieving this functionality by configuring the McBSP peripheral on the Delfino to use 3 frames of 16-bit words, and then using DMA to read in the data from the sensor. 

This works well for the most part.  Most of the time DMA correctly populates the correct memory locations with the corresponding 3 words.  However, my problem is that periodically (1 per 100,000 transactions or so) the words get swapped in memory.  After this, DMA continually populates the incorrect memory locations (word1 stored as word2, word2 stored as word3, word3 stored as word1).

I'm currently attempting to detect when this happens and then correcting the problem by resetting the McBSP and DMA peripherals.  I would prefer to fix the underlying problem so that it doesn't occur in the first place.

I'm monitoring the following error registers on the two peripherals but none of them show any recorded errors:

McbspbRegs.SPCR1.bit.RFULL

McbspbRegs.SPCR1.bit.RSYNCERR

McbspbRegs.SPCR2.bit.XSYNCERR

DmaRegs.CH3.CONTROL.bit.OVRFLG

DmaRegs.CH4.CONTROL.bit.OVRFLG

Below is the core code that implement the McBSP and DMA transactions.

//DMA memory allocation
// Place sdata2 and rdata2 buffers in DMA-accessible RAM
#pragma DATA_SECTION(sdata2, "DMARAML4")
#pragma DATA_SECTION(rdata2, "DMARAML4")
Uint16 sdata2; // Sent Data sensor 2
Uint16 rdata2; // Received Data sensor 2

//DMA register configuration
EALLOW;
DmaRegs.DMACTRL.bit.HARDRESET = 1;
asm(" NOP");
// Only 1 NOP needed per Design
EDIS;

transData = sdata2;
recvData = rdata2;

EALLOW;
DmaRegs.CH3.MODE.bit.CHINTE = 0;
// Channel 3, McBSPB transmit
DmaRegs.CH3.BURST_SIZE.all = 0; // 1 word/burst
DmaRegs.CH3.SRC_BURST_STEP = 0; // no effect when using 1 word/burst
DmaRegs.CH3.DST_BURST_STEP = 0; // no effect when using 1 word/burst
DmaRegs.CH3.TRANSFER_SIZE = 2; // Interrupt every 3 frames (3 bursts/transfer)
DmaRegs.CH3.SRC_TRANSFER_STEP = 1; // Move to next word in buffer after each word in a burst
DmaRegs.CH3.DST_TRANSFER_STEP = 0; // Don't move destination address
DmaRegs.CH3.SRC_ADDR_SHADOW = (Uint32) sdata2; // Start address = buffer
DmaRegs.CH3.SRC_BEG_ADDR_SHADOW = (Uint32) sdata2; // Not needed unless using wrap function
DmaRegs.CH3.DST_ADDR_SHADOW = (Uint32) &McbspbRegs.DXR1.all; // Start address = McBSPB DXR
DmaRegs.CH3.DST_BEG_ADDR_SHADOW = (Uint32) &McbspbRegs.DXR1.all; // Not needed unless using wrap function
DmaRegs.CH3.CONTROL.bit.PERINTCLR = 1; // Clear peripheral interrupt event flag
DmaRegs.CH3.CONTROL.bit.SYNCCLR = 1; // Clear sync flag
DmaRegs.CH3.CONTROL.bit.ERRCLR = 1; // Clear sync error_novotechnik flag
DmaRegs.CH3.DST_WRAP_SIZE = 0xFFFF; // Put to maximum - don't want destination wrap
DmaRegs.CH3.SRC_WRAP_SIZE = 0xFFFF; // Put to maximum - don't want source wrap
DmaRegs.CH3.MODE.bit.SYNCE = 0; // No sync signal
DmaRegs.CH3.MODE.bit.SYNCSEL = 0; // No sync signal
DmaRegs.CH3.MODE.bit.CHINTE = 1; // Enable channel interrupt
DmaRegs.CH3.MODE.bit.CHINTMODE = 1; // Interrupt at end of transfer
DmaRegs.CH3.MODE.bit.PERINTE = 1; // Enable peripheral interrupt event
DmaRegs.CH3.MODE.bit.PERINTSEL = DMA_MXREVTB; // Peripheral interrupt select = McBSP MXSYNCB
DmaRegs.CH3.MODE.bit.CONTINUOUS = 0; //continuous: (1) DMA re-initializes when TRANSFER_COUNT is zero and waits for the next interrupt event trigger
DmaRegs.CH3.CONTROL.bit.PERINTCLR = 1; // Clear any spurious interrupt flags

// Channel 4, McBSPB Receive
DmaRegs.CH4.MODE.bit.CHINTE = 0;
DmaRegs.CH4.BURST_SIZE.all = 0; // 1 word/burst
DmaRegs.CH4.SRC_BURST_STEP = 0; // no effect when using 1 word/burst
DmaRegs.CH4.DST_BURST_STEP = 0; // no effect when using 1 word/burst
DmaRegs.CH4.TRANSFER_SIZE = 2; // Interrupt every 3 frames (3 bursts/transfer)
DmaRegs.CH4.SRC_TRANSFER_STEP = 0; // Don't move source address
DmaRegs.CH4.DST_TRANSFER_STEP = 1; // Move to next word in buffer after each word in a burst
DmaRegs.CH4.SRC_ADDR_SHADOW = (Uint32) &McbspbRegs.DRR1.all; // Start address = McBSPB DRR
DmaRegs.CH4.SRC_BEG_ADDR_SHADOW = (Uint32) &McbspbRegs.DRR1.all; // Not needed unless using wrap function
DmaRegs.CH4.DST_ADDR_SHADOW = (Uint32) rdata2; // Start address = Receive buffer (for McBSP-B)
DmaRegs.CH4.DST_BEG_ADDR_SHADOW = (Uint32) rdata2; // Not needed unless using wrap function
DmaRegs.CH4.CONTROL.bit.PERINTCLR = 1; // Clear peripheral interrupt event flag
DmaRegs.CH4.CONTROL.bit.SYNCCLR = 1; // Clear sync flag
DmaRegs.CH4.CONTROL.bit.ERRCLR = 1; // Clear sync error_novotechnik flag
DmaRegs.CH4.DST_WRAP_SIZE = 0xFFFF; // Put to maximum - don't want destination wrap
DmaRegs.CH4.SRC_WRAP_SIZE = 0xFFFF; // Put to maximum - don't want source wrap
DmaRegs.CH4.MODE.bit.CHINTE = 1; // Enable channel interrupt
DmaRegs.CH4.MODE.bit.CHINTMODE = 1; // Interrupt at end of transfer
DmaRegs.CH4.MODE.bit.PERINTE = 1; // Enable peripheral interrupt event
DmaRegs.CH4.MODE.bit.PERINTSEL = DMA_MREVTB; // Peripheral interrupt select = McBSP MRSYNCA
DmaRegs.CH4.MODE.bit.CONTINUOUS = 0; //continuous: (1) DMA re-initializes when TRANSFER_COUNT is zero and waits for the next interrupt event trigger
DmaRegs.CH4.CONTROL.bit.PERINTCLR = 1; // Clear any spurious interrupt flags
EDIS;

//McBSP register configuration
// McBSP-B register settings
McbspbRegs.SPCR2.all = 0x0000; // Reset FS generator, sample rate generator & transmitter
McbspbRegs.SPCR1.all = 0x0000; // Reset Receiver, Right justify word, Digital loopback dis.
McbspbRegs.SPCR1.bit.DLB = 0;
McbspbRegs.SPCR1.bit.CLKSTP = 2; // clock stop mode bits (0-1: disabled, 2: clock stop with no clock delay, 3: clock stop with half-cycle delay)

McbspbRegs.PCR.bit.FSXM = 1; //transmit frame synch mode bit (0: external pin, 1: supplied by sample rate generator)
McbspbRegs.PCR.bit.FSRM = 1; //receive frame synch mode bit (0: external pin, 1: supplied by sample rate generator)
McbspbRegs.PCR.bit.CLKXM = 1; //transmit clock mode bit (in stop clock mode: 0: slave in SPI, 1: master in SPI)
McbspbRegs.PCR.bit.CLKRM = 1; //receive clock mode bit (in loopback mode: 0: MCLKR pin in high impedance state, 1: internal MCLKR driven by internal CLKX)
McbspbRegs.PCR.bit.SCLKME = 0; //sample rate generator input clock mode bit, used with CLKSM to select input clock
McbspbRegs.PCR.bit.DX_STAT = 0; //DX pin bit (0: drive DX pin low, 1: drive DX pin high)
McbspbRegs.PCR.bit.DR_STAT = 0; //DR pin status bit (0: DR pin is low, 1: DR pin is high)
McbspbRegs.PCR.bit.FSXP = 1; //transmit frame-synch polarity bit (0: transmit frame-synch pulses are active high, 1: active low)
McbspbRegs.PCR.bit.FSRP = 0; //receive frame-synch polarity bit (0: receive frame-synch pulses are active high, 1: active low)
McbspbRegs.PCR.bit.CLKXP = 0; //transmit clock polarity bit (transmit data sampled on 0: rising, 1: falling edge)
McbspbRegs.PCR.bit.CLKRP = 1; //receive clock polarity bit (transmit data sampled on 0: falling, 1: rising edge)

McbspbRegs.RCR1.bit.RWDLEN1 = 2; // 16-bit word receive
McbspbRegs.RCR1.bit.RFRLEN1 = 0; // 1-frame receptions
McbspbRegs.RCR2.bit.RDATDLY = 1; // FSX setup time 1 in master mode. 0 for slave mode (Receive)
McbspbRegs.RCR2.bit.RFIG = 1;

McbspbRegs.XCR1.bit.XWDLEN1 = 2; // 16-bit word transmit
McbspbRegs.XCR1.bit.XFRLEN1 = 0; // 1-frame tramsmissions
McbspbRegs.XCR2.bit.XDATDLY = 1; // FSX setup time 1 in master mode. 0 for slave mode (Transmit)
McbspbRegs.XCR2.bit.XFIG = 1;

McbspbRegs.SRGR2.all = 0x0000;
McbspbRegs.SRGR2.bit.CLKSM = 1; //sample rate generator input clock mode bit (used with SCLKME bit)
McbspbRegs.SRGR2.bit.FPER = 0; //frame synch period bits for FSG, FPER = 1 CLKG periods
McbspbRegs.SRGR1.bit.FWID = 0; // Frame Width = 1 CLKG period
McbspbRegs.SRGR1.bit.CLKGDV = 17; //divide-down value for CLKG, SPI_CLK_FREQ = LSPCLK / (CLKGDV+1) => 62.5mhz/18=3.47MHz spi clk

McbspbRegs.SPCR2.bit.GRST = 1; // Enable the sample rate generator
delay_loop(); // Wait at least 2 SRG clock cycles
McbspbRegs.SPCR2.bit.XRST = 1; // Release TX from Reset
McbspbRegs.SPCR1.bit.RRST = 1; // Release RX from Reset
McbspbRegs.SPCR2.bit.FRST = 1; // Frame Sync Generator reset


//start a new SPI read
void APS_Sensor::update() {
if(waitingForUpdate) {
return;
}

waitingForUpdate = 1;

if(McbspbRegs.SPCR1.bit.RFULL) {
MCBSP_RFULL_count++; //receive overrun condition, we've lost some data, not seeing any errors here
}
if(McbspbRegs.SPCR1.bit.RSYNCERR) {
MCBSP_RSYNCERR_count++; //not seeing any errors here
}
if(McbspbRegs.SPCR2.bit.XSYNCERR) {
MCBSP_XSYNCERR_count++; //not seeing any errors here
}

EALLOW;
if(DmaRegs.CH3.CONTROL.bit.OVRFLG) {
DMA1_OVRFLG_count++; //not seeing any errors here
}
if(DmaRegs.CH4.CONTROL.bit.OVRFLG) {
DMA2_OVRFLG_count++;  //not seeing any errors here
}
EDIS;
start_dma(); //start new SPI transaction 
}

void APS_Sensor::start_dma(void) {

GpioDataRegs.GPACLEAR.bit.GPIO15 = 1; //activate SS
EALLOW;
DmaRegs.CH3.CONTROL.bit.RUN = 1; // Start DMA Transmit from McBSP-B
DmaRegs.CH4.CONTROL.bit.RUN = 1; // Start DMA Receive from McBSP-B
EDIS;
}

// INT7.3
__interrupt void local_D_INTCH3_ISR(void) // DMA Ch3 (transmit)
{

EALLOW;
DmaRegs.CH3.CONTROL.bit.RUN = 0; // Re-enable DMA CH1. Should be done every transfer
EDIS;

PieCtrlRegs.PIEACK.all = PIEACK_GROUP7; // To receive more interrupts from this PIE group, acknowledge this interrupt

return;
}

// INT7.4
__interrupt void local_D_INTCH4_ISR(void) // DMA Ch4 (receive)
{
OutputAPS->newDataReceived();

EALLOW;
DmaRegs.CH4.CONTROL.bit.RUN = 0; // Re-enable DMA CH2. Should be done every transfer
EDIS;

PieCtrlRegs.PIEACK.all = PIEACK_GROUP7; // To receive more interrupts from this PIE group, acknowledge this interrupt

return;
}

Uint16 LA11_APS_Sensor::newDataReceived(void) {

LA11_26_APS_output.words.word1 = recvData[0];
LA11_26_APS_output.words.word2 = recvData[1];
LA11_26_APS_output.words.word3 = recvData[2];

nextSampleTime = addTime_uS(getCurrentTime(), LA11_FRAME_SPACING_US);

GpioDataRegs.GPASET.bit.GPIO15 = 1; //deactivate SS

waitingForUpdate = 0;

return 0;
}

Any suggestions on how to fix or further debug this problem would be appreciated. Thanks.

  • npaine,

    Please avoid dumping code into the body of the message. It is very difficult to manage while viewing threads. There is an "Insert code using SyntaxHighlighter" tool the Rich text editor of e2e which does help. Please use that if it is necessary to include code. Also, a lot of questions can be answered without looking at code. Debugging goes much better if you have specific questions we can answer and help guide you to the resolution. 

    With that out of the way, we can move on to your issue:

    Can you include a signal capture of your Chip select line? I noticed that your signal diagram is expecting 44 bit transmissions, but you are always sending 48. is this a problem or can the slave safely ignore the extra 4 clocks? i can see this causing a shift over time in the slave.

    Thanks,
    Mark

  • Mark, thank you for fixing the formatting of the code.

    I've only got a two channel scope to investigate this issue, so below is an image of the CS and MISO signals.  You can see that when CS is high, the MISO signal is in a Hi-Z state and when CS is low the MISO signal is driven either high or low.

      

    And heres the CLK and MISO signals again for comparison.  We can tell where the CS is low based on the Hi-Z state of the MISO signal.

    I don't think the 48 vs 44 clocks is causing the problem because the MISO waveform is consistent on the scope both before and after the problem occurs.

  • npaine,

    Have you been able to figure this out?

    Is there any event that happens that precedes the swap of the data?
    When you detect the swap, is are there any errors in the McBSP?
    Does a word get duplicated in the buffer at the "event" boundary?

    -Mark
  • npaine,

    Its been a few days since your last response. I am going to close this post. If it is not resolved, please reject this resolution and provide additional information so that we can continue the debug.

    Thanks,
    Mark
  • Mark, sorry for the delay. I'm not detecting McBSP errors (RFULL, RSYNCERR, or XSYNCERR) after the event. Nor do I see DMA1_OVRFLG or DMA2_OVERFLG.

    I'm not aware of any preceding event that triggers the swap event to occur.

    Reading the DMA memory buffer is the only way I know of to observe when the swap event occurs. This read is performed inside of the DMA Ch4 (receive) interrupt handler which I believe is triggered after the 3 frames of McBSP DRR has been copied into the DMA memory buffer by the DMA hardware. DmaRegs.CH4.DST_ADDR_SHADOW remains the same before and after the swap event, so it seems like the DMA's address pointer is pointing to the correct memory location.

    Any suggestions on how to dig deeper on this?
  • Hey there,

    You might try out some external loopback tests. Can you disconnect your slave, and then just transmit and receive what the McBSP is sending? if you run for a long period of time, does there same issue occur?

    Can you perhaps get your hands on an F2837x or F28004x device and use the SPI with DMA with 14-bit word size? Usually you want to specifically match up the word size correctly. I know that if the C2000 was the Slave, the words would be jumbled as you are sending more than the expected number of clocks. The C2000 SPI in slave mode will transmit data on every clock eventually leading to corruption as you then toggle the chip select pin.

    -Mark