Do you have new code that takes advantage of DMA? I just started looking into how to get this started as the current non-DMA throughput isn't high enough for our application.
I'm assuming the setup is going to be something like page 9 here: http://cache.freescale.com/files/32bit/ ... AN2867.pdf even though it's a different version of the processor. It looks like that should more or less replace what's happening in your ISR currently, mainly the manual loading of the TX FIFO and emptying of the RX FIFO.
Mod5441x and Nano54415 SPI driver available
Re: Mod5441x and Nano54415 SPI driver available
Time for a rather lengthy post…
After doing some digging it looks like enabling true DMA for DPI may only be possible for DSPI 1. First things first, though. It looks like the sim5441x.h definitions for the memory map are different than the user manual specifies. I rebuilt system files after changing them to:
When looking at the user manual I also noticed that there are only 16 DMA channels but there are 64 defined in table 19-6. I assume these are leftover from some earlier manual as I don't see any register configuration for these extra DMA channels and all channel configuration registers are 16 bits wide (one bit per channel). Assuming the table is still accurate for channels 0-15, it looks like only DSPI 0 and DSPI 1 are assigned DMA channels and since only DSPI 1 is brought out to the development board, it looks to me like that is the only channel truly capable of DMA SPI.
Continuing with the assumption that table 19-6 is accurate for DMA channels 0-15, I proceeded with some configuration code for enabling DMA for DSPI, passing in DSPI channel 1 (note that I’m transferring 16bits of data at a time and anything that follows assumes the same):
Now when DMA transfers are triggered, data should be moved four bytes at a time into the push register, which loads the transmit FIFO. One consideration is that the FIFO expects a word of command information in each dword, so the command data must be loaded into the buffer ahead of time. Since the intention is to use EOQ as the interrupt when the transfer is complete, that bit must be set for the final word transfer. The following code essentially puts the lowercase alphabet (repeating) into your buffer and adds command words where required:
Next we have to configure the DSPI to actually use DMA, which is done with the RSER register. In the DSPIStart routine (in dspi.cpp) I changed the RSER configuration to allow DMA requests for transmit and receive and still trigger an interrupt at the end of queue. Since the code above sets EOQ for the last frame, this will theoretically fire an interrupt after the last word of data is transferred. I just defined a new value and used that instead of the non-DMA version. Note that I also clear the EOQF bit here (before calling the ISR for the first time):
The final step is to modify the interrupt service routine (again, in dspi.cpp) to handle the start/stop of transfers correctly. This is called the first time by DSPIStart, which simply kicks off the transfer and sets a couple status variables. It should be called again when the transfer is complete, at which point we set some bits and status variables, and post the semaphore to signal that we’re done.
I rebuilt system files again to make sure the new ISR and start code is utilized. I added a call to my init_DSPI_DMA() function in my main before starting a DSPI transfer in the normal way, ie:
then wait for the transfer to finish with a small timeout:
Of course the semaphore always times out and I’m not able to see any data make it to the receive buffer so something is wrong. I think I have all the pieces in place but it appears I’m either misconfiguring or forgetting something. Anybody have thoughts?
After doing some digging it looks like enabling true DMA for DPI may only be possible for DSPI 1. First things first, though. It looks like the sim5441x.h definitions for the memory map are different than the user manual specifies. I rebuilt system files after changing them to:
Code: Select all
/*
* EDMA TRANSFER CONTROL DESCRIPTOR (tcd[16] = 0xFC04_5000 -> 0xFC04_51FF)
*/
typedef struct {
vudword saddr; /* 0x0000 -> 0x0003 - Source Address */
vuword attr; /* 0x0004 -> 0x0005 - Transfer Attributes */
vuword soff; /* 0x0006 -> 0x0007 - Signed Source Address Offset */
vudword nbytes; /* 0x0008 -> 0x000B - Signed Minor Loop Offset/Minor Byte Count */
vudword slast; /* 0x000C -> 0x000F - Last Source Address Adjustment */
vudword daddr; /* 0x0010 -> 0x0013 - Destination Address */
vuword citer; /* 0x0014 -> 0x0015 - Current Minor Loop Link/Major Loop Count */
vuword doff; /* 0x0016 -> 0x0017 - Signed Destination Address Offset */
vudword dlast_sga; /* 0x0018 -> 0x001B - Last Destination Addr. Adjustment/Scatter Gather Addr. */
vuword biter; /* 0x001C -> 0x001D - Beginning Minor Loop Link/Major Loop Count */
vuword csr; /* 0x001E -> 0x001F - Control and Status */
} edma_tcdstruct;
/*
* ENHANCED DIRECT MEMORY ACCESS (EDMA) CONTROLLER
*/
typedef struct {
vudword cr; /* 0xFC04_4000 -> 0xFC04_4003 - eDMA Control Register */
vudword es; /* 0xFC04_4004 -> 0xFC04_4007 - eDMA Error Status Register */
vubyte pack00[6]; /* 0xFC04_4008 -> 0xFC04_400D - RESERVED */
vuword erq; /* 0xFC04_400E -> 0xFC04_400F - eDMA Enable Request Register */
vubyte pack01[6]; /* 0xFC04_4010 -> 0xFC04_4015 - RESERVED */
vuword eei; /* 0xFC04_4016 -> 0xFC04_4017 - eDMA Enable Error Interrupt Register */
vubyte serq; /* 0xFC04_4018 -> 0xFC04_4018 - eDMA Set Enable Request */
vubyte cerq; /* 0xFC04_4019 -> 0xFC04_4019 - eDMA Clear Enable Request */
vubyte seei; /* 0xFC04_401A -> 0xFC04_401A - eDMA Set Enable Error Interrupt Register */
vubyte ceei; /* 0xFC04_401B -> 0xFC04_401B - eDMA Clear Enable Error Interrupt Register */
vubyte cint; /* 0xFC04_401C -> 0xFC04_401C - eDMA Clear Interrupt Request Register */
vubyte cerr; /* 0xFC04_401D -> 0xFC04_401D - eDMA Clear Error Register */
vubyte ssrt; /* 0xFC04_401E -> 0xFC04_401E - eDMA Set START Bit Register */
vubyte cdne; /* 0xFC04_401F -> 0xFC04_401F - eDMA Clear DONE Status Bit Register */
vubyte pack02[6]; /* 0xFC04_4020 -> 0xFC04_4025 - RESERVED */
vudword intr; /* 0xFC04_4026 -> 0xFC04_4029 - eDMA Interrupt Request Register */
vubyte pack03[4]; /* 0xFC04_402A -> 0xFC04_402D - RESERVED */
vudword err; /* 0xFC04_402E -> 0xFC04_4031 - eDMA Error Register */
vubyte pack04[206]; /* 0xFC04_4032 -> 0xFC04_40FF - RESERVED */
vubyte dchpri[16]; /* 0xFC04_4100 -> 0xFC04_410F - eDMA Channel 0-15 Priority Registers */
vubyte pack05[3824]; /* 0xFC04_4110 -> 0xFC04_4FFF - RESERVED */
edma_tcdstruct tcd[16]; /* 0xFC04_5000 -> 0xFC04_51FF - Transfer Control Descriptor 0-15 */
} edmastruct;
Continuing with the assumption that table 19-6 is accurate for DMA channels 0-15, I proceeded with some configuration code for enabling DMA for DSPI, passing in DSPI channel 1 (note that I’m transferring 16bits of data at a time and anything that follows assumes the same):
Code: Select all
#define DSPI0_BASE 0xFC05C000 // base address for DSPI 0
#define DSPI1_BASE 0xFC03C000 // base address for DSPI 1
#define DSPI2_BASE 0xEC038000 // base address for DSPI 2
#define DSPI3_BASE 0xEC03C000 // base address for DSPI 3
#define PUSH_REG_OFFSET 0x34 // address offset of the push transmit FIFO register
#define POP_REG_OFFSET 0x38 // address offset of the pop receive FIFO register
/*------------------------------------------------------------------
* Initialize DMA transfers for a DSPI channel
*-----------------------------------------------------------------*/
void init_DSPI_DMA(void* txbuf, void* rxbuf, int dspi_chan)
{
int dspi_tx_chan;
int dspi_rx_chan;
int i, push_reg, pop_reg;
// Get the correct DMA channels and push/pop addresses for selected DSPI channel
switch (dspi_chan)
{
case 0:
push_reg = DSPI0_BASE + PUSH_REG_OFFSET;
pop_reg = DSPI0_BASE + POP_REG_OFFSET;
dspi_tx_chan = 13;
dspi_rx_chan = 12;
break;
case 2:
push_reg = DSPI2_BASE + PUSH_REG_OFFSET;
pop_reg = DSPI2_BASE + POP_REG_OFFSET;
dspi_tx_chan = 29;
dspi_rx_chan = 28;
break;
case 3:
push_reg = DSPI3_BASE + PUSH_REG_OFFSET;
pop_reg = DSPI3_BASE + POP_REG_OFFSET;
dspi_tx_chan = 45;
dspi_rx_chan = 44;
break;
default: // case 1 and any other bogus case
push_reg = DSPI1_BASE + PUSH_REG_OFFSET;
pop_reg = DSPI1_BASE + POP_REG_OFFSET;
dspi_tx_chan = 15;
dspi_rx_chan = 14;
}
// Configure fixed priorities and disable debug mode
sim2.edma.cr = 0;
// Clear enable request register before initializing tcd
// Otherwise errors could be triggered before configuration is complete
sim2.edma.erq = 0;
// Clear error interrupt enable register
sim2.edma.eei = 0;
// Set the priorities of the channels, higher channels have lower priorities
for (i=0; i<16; i++)
sim2.edma.dchpri[i] = 15-i;
// Transfer Control Descriptor for DSPI TFFF (transmit FIFO fill flag)
sim2.edma.tcd[dspi_tx_chan].saddr = (int)txbuf; // 32b - transmit buffer address
sim2.edma.tcd[dspi_tx_chan].attr = 0x0202; // 16b - addresses modulo = 0, 32 bit transfers
sim2.edma.tcd[dspi_tx_chan].soff = 4; // 16b - offset to next transfer address
sim2.edma.tcd[dspi_tx_chan].nbytes = 4; // 32b - bytes to transfer per service request
sim2.edma.tcd[dspi_tx_chan].slast = 0; // 32b - no source offset for last transfer
sim2.edma.tcd[dspi_tx_chan].daddr = push_reg; // 32b - DSPI push transmit FIFO register
sim2.edma.tcd[dspi_tx_chan].citer = 1; // 16b - current iteration (disabled)
sim2.edma.tcd[dspi_tx_chan].doff = 0; // 16b - writing to same register each time, so no offset
sim2.edma.tcd[dspi_tx_chan].dlast_sga = 0; // 32b - no destination offset for last transfer
sim2.edma.tcd[dspi_tx_chan].biter = 1; // 16b - beginning major iteration count
sim2.edma.tcd[dspi_tx_chan].csr = 0; // 16b - no stalls, disable random junk
// Transfer Control Descriptor for DSPI RFDF (receive FIFO drain flag)
sim2.edma.tcd[dspi_rx_chan].saddr = pop_reg; // 32b - transmit buffer address
sim2.edma.tcd[dspi_rx_chan].attr = 0x0202; // 16b - addresses modulo = 0, 32 bit transfers
sim2.edma.tcd[dspi_rx_chan].soff = 0; // 16b - reading same register each time, so no offset
sim2.edma.tcd[dspi_rx_chan].nbytes = 4; // 32b - bytes to transfer per service request
sim2.edma.tcd[dspi_rx_chan].slast = 0; // 32b - no source offset for last transfer
sim2.edma.tcd[dspi_rx_chan].daddr = (int)rxbuf; // 32b - DSPI push transmit FIFO register
sim2.edma.tcd[dspi_rx_chan].citer = 1; // 16b - current iteration (disabled)
sim2.edma.tcd[dspi_rx_chan].doff = 4; // 16b - offset to next transfer address
sim2.edma.tcd[dspi_rx_chan].dlast_sga = 0; // 32b - no destination offset for last transfer
sim2.edma.tcd[dspi_rx_chan].biter = 1; // 16b - beginning major iteration count
sim2.edma.tcd[dspi_rx_chan].csr = 0; // 16b - no stalls, disable random junk
// Enable interrupts on channels dspi_tx_chan and dspi_rx_chan
sim2.edma.erq = (1 << dspi_tx_chan) | (1 << dspi_rx_chan);
//iprintf("ERQ:%04x\n",sim2.edma.erq);
//iprintf("ES:%08x\n",sim2.edma.es);
}
Code: Select all
// For DMA, there are 2 bytes command data and 2 bytes data data
// Interleave the data
for (i=0;i<sizeof(TXBuffer_1);i+=4)
{
TXBuffer_1[i+0] = (i/2)%26+97; // start at 'a'
TXBuffer_1[i+1] = (i/2)%26+98; // start at 'b'
TXBuffer_1[i+2] = 0;
TXBuffer_1[i+3] = 0x80; // keep CS asserted between transfer, no EOQ
}
TXBuffer_1[i-1] = 0x08; // de-assert CS after final transfer, EOQ
Code: Select all
#define RSER_EOQF_WITH_DMA 0x13030000
//spi->rser = RSER_EOQF_IRQ_ONLY;
spi->rser = RSER_EOQF_WITH_DMA;
spi->sr |= SR_EOQF_MASK;
Code: Select all
/*-----------------------------------------------------------------------
DSPI interrupt service routine.
Called at kickoff and by hardware when DSPI transfer complete
------------------------------------------------------------------------*/
void DSPI_Isr_new( unsigned int moduleNum, volatile dspistruct *spiModule )
{
register volatile dspistruct &spi = *spiModule;
//iprintf("DSPI ISR - SR: %08x, RSER: %08X\n",spi.sr,spi.rser);
if(spi.sr & SR_EOQF_MASK) // transfer complete
{
// do not clear the EOQ flag until *AFTER* setting the halt bit
// see MCF54418 manual section 40.4.1: Start and stop of DSPI transfers
spi.mcr |= MCR_HALT_BIT;
spi.sr &= SR_CLR_FLAGS;
driverCxt[moduleNum].DSPI_INT_STATUS = DSPI_OK;
driverCxt[moduleNum].DSPIfinished = TRUE;
if(driverCxt[moduleNum].DSPI_Sem)
OSSemPost( driverCxt[moduleNum].DSPI_Sem );
}
else // start transfer
{
spi.mcr &= ~MCR_HALT_BIT;
driverCxt[moduleNum].DSPI_INT_STATUS = DSPI_BUSY;
driverCxt[moduleNum].DSPIfinished = FALSE;
}
return;
}
Code: Select all
DSPIStart(1,(BYTE*)TXBuffer_1,(BYTE*)RXBuffer_1,NUM_BYTES,&DSPI_SEM_1);
Code: Select all
OSSemPend(&DSPI_SEM_1, 2);
Re: Mod5441x and Nano54415 SPI driver available
Jch, first off, you've done quite a lot of digging in there (It's fairly deep...) so congrats. I'm currently trying to get the DMA driven driver integrated into our MMC driver for accessing the SD card. This is also a pretty good test of the driver. I would have posted this portion of the driver sooner, except... I'm having issues with it.
I'll say this, if you're using the MCF54415RM reference manual from the install, or from our website, you're not going to get the DMA working, period. My apologies there. The reason is that Freescale has pretty seriously dropped the ball and that entire chapter is wrong in Rev. 4 of the manual. There are in fact 64 channels on the processor. There are also registers with bits not listed in Rev 4, that if you change will prevent the DMAC from running. You'll want to use Rev. 3 of the reference manual (Download Here; Freescale Forum Thread).
As for actually utilizing the DMA, I've setup the driver to only read/write 8 to 16 bits to the popr/pushr registers. This means we'll only be using CTAR[0] for our framesize/baudrate/etc. It also means you can't set any chip selects active. The workaround is to use the MCR register to invert the inactive state while actually doing the transaction. Finally, it also means you can't set the EOQ bit. In order to actually be informed when the transfer is complete, the driver uses the DMA ISR to tickle the wakeup semaphore after the RX channel finishes draining the RX FIFO. Oh, and one last issue is that I've disabled the TX FIFO to prevent the RX FIFO from overflowing at HIGH (aka, max) baud.
I've attached the current status of the driver; I apologize if it feels weirdly laid out/lack of comments, but I'm kind of tearing it apart at the moment to find my issue
I'll say this, if you're using the MCF54415RM reference manual from the install, or from our website, you're not going to get the DMA working, period. My apologies there. The reason is that Freescale has pretty seriously dropped the ball and that entire chapter is wrong in Rev. 4 of the manual. There are in fact 64 channels on the processor. There are also registers with bits not listed in Rev 4, that if you change will prevent the DMAC from running. You'll want to use Rev. 3 of the reference manual (Download Here; Freescale Forum Thread).
As for actually utilizing the DMA, I've setup the driver to only read/write 8 to 16 bits to the popr/pushr registers. This means we'll only be using CTAR[0] for our framesize/baudrate/etc. It also means you can't set any chip selects active. The workaround is to use the MCR register to invert the inactive state while actually doing the transaction. Finally, it also means you can't set the EOQ bit. In order to actually be informed when the transfer is complete, the driver uses the DMA ISR to tickle the wakeup semaphore after the RX channel finishes draining the RX FIFO. Oh, and one last issue is that I've disabled the TX FIFO to prevent the RX FIFO from overflowing at HIGH (aka, max) baud.
I've attached the current status of the driver; I apologize if it feels weirdly laid out/lack of comments, but I'm kind of tearing it apart at the moment to find my issue
- Attachments
-
- DSPI_DMA_WORK_IN_PROGRESS.zip
- (32.41 KiB) Downloaded 305 times
Dan Ciliske
Project Engineer
Netburner, Inc
Project Engineer
Netburner, Inc
Re: Mod5441x and Nano54415 SPI driver available
The manual being wrong certainly explains a lot, and is pretty much what I was expecting given its DMA channel description inconsistencies. Thanks a bunch for the links and I'll dig in to your new code ASAP.