Forum Discussion

Altera_Forum's avatar
Altera_Forum
Icon for Honored Contributor rankHonored Contributor
15 years ago

DMA 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's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    --- Quote Start ---

    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.

    --- Quote End ---

    Oh, sorry I forgot!:-)

    --- Quote Start ---

    Which version of Quartus and Nios EDS do you use?

    --- Quote End ---

    I use Quartus 10.0 and NIOS EDS 10.0. Both with SP1.

    It's really strange that you get almost correct transfer though you are using (in my opinion) the wrong address of the TXDATA register of the UART.

    Hmmm? No Idea yet. Sorry...

    Maybe you should use SignalTap for debugging. It helped us a lot! Really amazing tool.

    Regards
  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    Hi!

    I have the same problem with Quartus 10.0 SP1 and NIOS EDS 10.0 SP1.

    Can you post your working code for UART to RAM transfer?

    Thanks!

    Regards
  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    Hi,

    I hope this will help you. But I am using an ISR for servicing a completed DMA-Transfer.

    Int32 Transmit(Char8 * pTx, Int32 lLength)
    {
        //softwarereset
        IOWR_ALTERA_AVALON_DMA_CONTROL(DMA_RS485_BASE, 0x1000);
        IOWR_ALTERA_AVALON_DMA_CONTROL(DMA_RS485_BASE, 0x1000);
        IOWR_ALTERA_AVALON_PIO_DATA    (PIO_RS485_BASE,TRANSMIT); // setting the direction of the transceiver using a PIO
        IORD_ALTERA_AVALON_UART_RXDATA(UART_RS485_BASE); //dummy read to UART
        IOWR_ALTERA_AVALON_DMA_CONTROL(DMA_RS485_BASE,
                                                 ALTERA_AVALON_DMA_CONTROL_WCON_MSK|
                                                 ALTERA_AVALON_DMA_CONTROL_BYTE_MSK|
                                                 ALTERA_AVALON_DMA_CONTROL_LEEN_MSK|
                                                 ALTERA_AVALON_DMA_CONTROL_I_EN_MSK); //setzen von WCON
        DmaTransfer                              ((Int32) IOADDR_ALTERA_AVALON_UART_TXDATA(UART_RS485_BASE),
                                                (Int32) pTx, lLength);
        return 0;
    }
    Int32 Receive(Char8 * pRx, Int32 lLength)
    {
        while (!(IORD_ALTERA_AVALON_UART_STATUS(UART_RS485_BASE)
                    & ALTERA_AVALON_UART_STATUS_TMT_MSK));
        //softwarereset
        IOWR_ALTERA_AVALON_DMA_CONTROL(DMA_RS485_BASE, 0x1000);
        IOWR_ALTERA_AVALON_DMA_CONTROL(DMA_RS485_BASE, 0x1000);
        IORD_ALTERA_AVALON_UART_RXDATA(UART_RS485_BASE); // dummy read to UART
        IOWR_ALTERA_AVALON_DMA_CONTROL(DMA_RS485_BASE,
                                                 ALTERA_AVALON_DMA_CONTROL_RCON_MSK|
                                                 ALTERA_AVALON_DMA_CONTROL_BYTE_MSK|
                                                 ALTERA_AVALON_DMA_CONTROL_LEEN_MSK|
                                                 ALTERA_AVALON_DMA_CONTROL_I_EN_MSK);
        IOWR_ALTERA_AVALON_PIO_DATA    (PIO_RS485_BASE,RECEIVE); // setting the direction of the transceiver using a PIO
        DmaTransfer                            ((Int32)pRx,
                                                 (Int32)IOADDR_ALTERA_AVALON_UART_RXDATA(UART_RS485_BASE),
                                                 lLength);
        return 0;
    }
    void DmaTransfer(Int32 lWhere, Int32 lFrom, Int32 lHowMuch)
    {
        while ((IORD_ALTERA_AVALON_DMA_STATUS(DMA_RS485_BASE)
                 & ALTERA_AVALON_DMA_STATUS_BUSY_MSK));
        IOWR_ALTERA_AVALON_DMA_STATUS        (DMA_RS485_BASE, 0);
        IOWR_ALTERA_AVALON_DMA_RADDRESS    (DMA_RS485_BASE,(Int32) lFrom);
        IOWR_ALTERA_AVALON_DMA_WADDRESS    (DMA_RS485_BASE,(Int32) lWhere);
        IOWR_ALTERA_AVALON_DMA_LENGTH        (DMA_RS485_BASE, lHowMuch);
        IOWR_ALTERA_AVALON_DMA_CONTROL    (DMA_RS485_BASE,
                                                     ALTERA_AVALON_DMA_CONTROL_GO_MSK |
                                                     IORD_ALTERA_AVALON_DMA_CONTROL(DMA_RS485_BASE));
    }
    Good luck!