Forum Discussion
Altera_Forum
Honored Contributor
16 years agoDMA transfer inside ISR: Problem
Hi everybody,
I think this is a common issue, but I didn't find a solution to my question, so hopefully I will get an answer here: I have a DMA controller which I want to use on an IRQ coming from a PIO. The DMA works fine and also the IRQ does. But when I try to call the DMA transfer function INSIDE the ISR from the PIO, the DMA doesn't send it's Interrupt at the end (when transfer is finished). Priority of PIO Interrupt is 7, Priority of DMA is 6 (so there should not be a priority problem). Does anyone of you know a solution to my problem? Here is my code:
volatile static int rx_done = 0;
static void done_DMA(void* handle, void* data)
{
rx_done = 1;
}
void measureIOs()
{
int rc;
alt_dma_txchan txchan;
alt_dma_rxchan rxchan;
unsigned long* tx_data = (void*) EXT_DPR_TRISTATE_INTERFACE_0_BASE; // pointer to data to send
unsigned long* rx_buffer = (void*) SDRAM_BASE + 0x100000; // pointer to rx buffer
rx_buffer = (void*) SDRAM_BASE + 0x100000;
rx_done = 0;
// Create the transmit channel
if ((txchan = alt_dma_txchan_open("/dev/dma_0")) == NULL)
{
printf ("Failed to open transmit channel\n");
exit (1);
}
// Create the receive channel
if ((rxchan = alt_dma_rxchan_open("/dev/dma_0")) == NULL)
{
printf ("Failed to open receive channel\n");
exit (1);
}
if(alt_dma_txchan_ioctl(txchan,ALT_DMA_SET_MODE_32,NULL)<0)
{
exit(1);
}
if(alt_dma_rxchan_ioctl(rxchan,ALT_DMA_SET_MODE_32,NULL)<0)
{
exit(1);
}
if(alt_dma_txchan_ioctl(txchan,ALT_DMA_TX_ONLY_OFF,NULL)<0)
{
exit(1);
}
if(alt_dma_rxchan_ioctl(rxchan,ALT_DMA_RX_ONLY_OFF,NULL)<0)
{
exit(1);
}
// Post the transmit request
if ((rc = alt_dma_txchan_send (txchan,tx_data,4096,NULL,NULL)) < 0)
{
printf ("Failed to post transmit request, reason = %i\n", rc);
exit (1);
}
// Post the receive request
if ((rc = alt_dma_rxchan_prepare (rxchan,rx_buffer,4096,done_DMA,NULL)) < 0)
{
printf ("Failed to post read request, reason = %i\n", rc);
exit (1);
}
// wait for transfer to complete
while (!rx_done);
printf ("Transfer successful!\n");
}
/* INTERRUPT VARIABLE DEFINITONS */
volatile int edge_capture;
/* **************** INTERRUPT FUNCTIONS ********************* */
# ifdef PIO_SELECTED_RAM_BASE
# ifdef ALT_ENHANCED_INTERRUPT_API_PRESENT
static void handle_ram_interrupt(void* context)
# else
static void handle_ram_interrupt(void* context, alt_u32 id)
# endif
{
/* Cast context to edge_capture's type. It is important that this be
* declared volatile to avoid unwanted compiler optimization.
*/
volatile int* edge_capture_ptr = (volatile int*) context;
/* Store the value in the Button's edge capture register in *context. */
*edge_capture_ptr = IORD_ALTERA_AVALON_PIO_EDGE_CAP(PIO_SELECTED_RAM_BASE);
/* Reset the Button's edge capture register. */
IOWR_ALTERA_AVALON_PIO_EDGE_CAP(PIO_SELECTED_RAM_BASE, 0);
measureIOs();
/*
* Read the PIO to delay ISR exit. This is done to prevent a spurious
* interrupt in systems with high procekssor -> pio latency and fast
* interrupts.
*/
IORD_ALTERA_AVALON_PIO_EDGE_CAP(PIO_SELECTED_RAM_BASE);
}
/* Initialize the ram_pio. */
static void init_ram_pio()
{
/* Recast the edge_capture pointer to match the alt_irq_register() function
* prototype. */
void* edge_capture_ptr = (void*) &edge_capture;
/* Enable all 4 button interrupts. */
IOWR_ALTERA_AVALON_PIO_IRQ_MASK(PIO_SELECTED_RAM_BASE, 0x1);
/* Reset the edge capture register. */
IOWR_ALTERA_AVALON_PIO_EDGE_CAP(PIO_SELECTED_RAM_BASE, 0x0);
/* Register the interrupt handler. */
# ifdef ALT_ENHANCED_INTERRUPT_API_PRESENT
alt_ic_isr_register(PIO_SELECTED_RAM_IRQ_INTERRUPT_CONTROLLER_ID,PIO_SELECTED_RAM_IRQ,
handle_ram_interrupt, edge_capture_ptr, 0x0);
# else
alt_irq_register( PIO_SELECTED_RAM_IRQ, edge_capture_ptr,
handle_ram_interrupt);
# endif
}
# endif /***************** PIO_SELECTED_RAM_BASE **********************/
int main()
{
printf("Hello from Nios II!\n");
init_ram_pio();
while(1);
return 0;
}
Thanks for all answers ! =)23 Replies
- Altera_Forum
Honored Contributor
Hi! Thank you so much for your fast response!
Yes, in my system I have a processor NiosII/f with data and istruction cache (look the attachment). In my system I use an external SRAM and a little on_chip_memory (4096 Bytes) which is used also for exception vector. If I read the address of buffer it seems that it's always allocated in external SRAM in all previous cases. How can I bypass the cache and read the values direct from the ram?! Thanks for your help! (http://www.answerbag.com/q_view/1182695#ixzz13xxzrkvj) - Altera_Forum
Honored Contributor
You are welcome!
For example you have the following possibilities for bypassing the cache. 1. You can use the IOWR/IORD(or the 16/8Bit versions)-Macros for direct memory access of your data. But in this case you have always to use IORD/IOWR to read/write valid data. The data will still be cached but as long as you use IORD/IOWR everything should work fine. 2. Use alt_uncached_malloc() / alt_uncached_free() . In this case you will get a pointer to data which will not be cached. (Remark: Bit32 of your pointer will be set to indicate that it is uncached) I have already used both. With Alternative 2 you can more easily access your memory as you were used to. But you have to ensure that you free the memory after you don't need it anymore. Regards - Altera_Forum
Honored Contributor
For (1) you need to ensure that the cache line is invalid at the start.
For (2) you need to ensure that the ends of the memory block aren't shared with anything else. - Altera_Forum
Honored Contributor
Thanks very much!
Both the solutions work! Thanks for your support and have a nice day! Regards - Altera_Forum
Honored Contributor
Hi DSL!
--- Quote Start --- For (1) you need to ensure that the cache line is invalid at the start. For (2) you need to ensure that the ends of the memory block aren't shared with anything else. --- Quote End --- For (1): why the cache line have to be INVALID (?!) at the start? What this means? And how I could test it? For (2): how can I ensure that the memory block aren't shared with anything else? This work is not made by the compiler? Regards - Altera_Forum
Honored Contributor
Another question:
I try also to transmit data from a variable (placed in the SRAM) to the UART with a DMA. I write a easy test software, but the result is strange. This is the code: printf("Hello from DMA?!\n"); IOWR_ALTERA_AVALON_UART_DIVISOR(SERIAL_UART_BASE,108); // Baud Rate: 921600 @ 100Mhz alt_u8 *tx_buffer = alt_uncached_malloc(BUFF_SIZE * sizeof (alt_u8)); tx_buffer = "Hello World\0"; printf("tx buffer position: 0x%X \n",tx_buffer); IOWR_ALTERA_AVALON_DMA_STATUS(DMA_BASE, 0); IOWR_ALTERA_AVALON_DMA_RADDRESS(DMA_BASE, tx_buffer); IOWR_ALTERA_AVALON_DMA_WADDRESS(DMA_BASE, SERIAL_UART_BASE+1); // TxData address is @ SERIAL_UART_BASE + 1(offset) IOWR_ALTERA_AVALON_DMA_LENGTH(DMA_BASE, 12); IOWR_ALTERA_AVALON_DMA_CONTROL(DMA_BASE, 0x289); usleep (500000); printf("status: 0x%X \n",IORD_ALTERA_AVALON_DMA_STATUS(DMA_BASE)); printf("read add: 0x%X \n",IORD_ALTERA_AVALON_DMA_RADDRESS(DMA_BASE)); printf("write add: 0x%X \n",IORD_ALTERA_AVALON_DMA_WADDRESS(DMA_BASE)); printf("length: 0x%X \n",IORD_ALTERA_AVALON_DMA_LENGTH(DMA_BASE)); printf("control: 0x%X \n",IORD_ALTERA_AVALON_DMA_CONTROL(DMA_BASE)); The DMA write 2* '\0' chars and 3* 'H' chars (look the attachment). Why? The console output is right: Hello from DMA?! tx buffer position: 0x288F638 status: 0x11 // Operation Done - len = 0 read add: 0x288F644 // 0x288F638 + C(12) write add: 0x2903461 length: 0x0 control: 0x289 Regards - Altera_Forum
Honored Contributor
You are using Altera's UART?
--- Quote Start --- IOWR_ALTERA_AVALON_DMA_WADDRESS(DMA_BASE, SERIAL_UART_BASE+1); // TxData address is @ SERIAL_UART_BASE + 1(offset) --- Quote End --- I think this is not right. Register offset is 1, but the width is 32 bit. So next register address should be BASE + 4. Try to use this:
Strange you see anything at all...... RegardsIOWR_ALTERA_AVALON_DMA_WADDRESS(DMA_BASE, IOADDR_ALTERA_AVALON_UART_TXDATA(SERIAL_UART_BASE)); - Altera_Forum
Honored Contributor
Hi!
Yes, I'm using Altera UART. I try to use IOADDR_ALTERA_AVALON_UART_TXDATA(SERIAL_UART_BASE), but with this command the UART doesn't work, so I try to use a different BASE and with SERIAL_UART_BASE + 1 it works. So strange... my SERIAL UART BASE is: 0x2903460 SERIAL_UART_BASE + 1 give me the address: 0x2903461 and IOADDR_ALTERA_AVALON_UART_TXDATA(SERIAL_UART_BASE) give me the address: 0x2903464 - Altera_Forum
Honored Contributor
--- Quote Start --- and IOADDR_ALTERA_AVALON_UART_TXDATA(SERIAL_UART_BASE) give me the address: 0x2903464 --- Quote End --- That's the correct address. I am using the same for my DMA->UART transfer which works pretty fine. Take this address, because calling IOADDR_ALTERA_AVALON_UART_TXDATA(SERIAL_UART_BASE) gives you the correct address. --- Quote Start --- IOWR_ALTERA_AVALON_DMA_CONTROL(DMA_BASE, 0x289); --- Quote End --- I think you forgot to set I_EN in the control register? Set it and try again. - Altera_Forum
Honored Contributor
Hi!
No, I don't forget to set I_EN. I don't enable the IRQ because I'll not use a ISR routine in my project to transmit the data from DMA to UART. Which version of Quartus and Nios EDS do you use? Tomorrow I will install Quartus 10 and Nios EDS and I will recompile the project in the new version (now I used the 9.1 sp2). I hope that it’s a compiler error…. Thanks for your help!