Forum Discussion

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

SGDMA Interrupt

Hi,

I'm having a major issue with the SGDMA component - the callback function that I've defined does not run. I'm not sure whether my code is flawed and I'm not calling the ISR function correctly, or I've set up the SGDMA wrong and it isn't generating an interrupt.

I'm using the SGDMA to stream to memory the data from a trigger component connected to an ADC (which only streams if the input signal passes a certain threshold). I'm pretty sure the issue is code based, but I can provide the Qsys file if needed.

The only problem that I can find is that the alt_avalon_sgdma_check_descriptor_status returns an error code of 119 - I've looked it up in errno.h, but I'm not sure how to solve it ("Connection already in progress ").

Here's the code I'm using:

#include <stdio.h># include <system.h># include <unistd.h># include "LCD_setup.h"# include "peripheral_tests.h"# include "ringBufS.h"# include <sys/alt_irq.h># include "altera_up_avalon_rs232.h"# include "altera_avalon_sgdma_regs.h"# include "altera_avalon_sgdma.h"# include "altera_avalon_sgdma_descriptor.h"# include "errno.h"# include "sys/alt_irq.h"
//alt_u32 * write_addr ;
const alt_u16 NUM_BYTES_TO_DMA = 32768 ;
const alt_u32 CIRCULAR_LOOP_LENGTH = 32768*128;
//define structure to pass data from main to ISR
typedef struct
{
    int cnt;
    alt_sgdma_dev *pSGDMA;
    alt_u32 * write_addr;
    int debug1;
    int debug2;
    int debug3;
} t_MyContext;
//ISR routine
void isr_sgdma_routine (t_MyContext * c, alt_u32 id){
    alt_sgdma_descriptor *desc = (alt_sgdma_descriptor *) DESCRIPTOR_MEMORY_0_BASE  ;
    alt_sgdma_descriptor *next = (alt_sgdma_descriptor *) DESCRIPTOR_MEMORY_0_BASE  + sizeof(alt_sgdma_descriptor);
    int *green_leds = LEDG_BASE;
    //reset interrupt
    IOWR_ALTERA_AVALON_SGDMA_CONTROL(c->pSGDMA, IORD_ALTERA_AVALON_SGDMA_CONTROL(c->pSGDMA) |  ALTERA_AVALON_SGDMA_CONTROL_CLEAR_INTERRUPT_MSK );
    c->cnt++;
    green_leds=0xAA;
    //clear interrupt (useful ?)
    IOWR_ALTERA_AVALON_SGDMA_CONTROL(c->pSGDMA, IORD_ALTERA_AVALON_SGDMA_CONTROL(c->pSGDMA) |  ALTERA_AVALON_SGDMA_CONTROL_CLEAR_INTERRUPT_MSK );
    //to reduce over-run risk
    if (c->cnt < 150)
    {
            //get last descriptor of the chain (TODO)
            if (desc->actual_bytes_transferred != NUM_BYTES_TO_DMA)
            {
                c->debug1 ++;
                //construct a "bad descriptor" in order to reset the descriptor "desc"
                alt_avalon_sgdma_construct_stream_to_mem_desc(desc,next,0,NUM_BYTES_TO_DMA,0);
                //stop DMA
                alt_avalon_sgdma_stop(c->pSGDMA);
            }
            else
            {
            c->write_addr = c->write_addr + (desc->actual_bytes_transferred / 4) ;
            if (c->write_addr >= CIRCULAR_LOOP_LENGTH)
                {
                    c->debug2++;
                    c->write_addr = 0;
                }
                alt_avalon_sgdma_construct_stream_to_mem_desc(desc,next,c->write_addr,NUM_BYTES_TO_DMA,0);
                if(alt_avalon_sgdma_do_async_transfer(c->pSGDMA,desc) == -EBUSY )
                    c->debug3++;
            }
    }
    else
    {
        alt_avalon_sgdma_stop(c->pSGDMA);
    }
    return;
}
int main() {
    int *green_leds = LEDG_BASE;
    green_leds=0xFF;
    //declare pointer to ISR
    void* isr_sgdma_routine_ptr = (void*) &isr_sgdma_routine;
    //set ADC channels active
    int *adc_reg = ADC_IF_0_BASE;
    adc_reg=1;
    adc_reg=1;
    //set trigger levels
    int *trigger_reg = TEST_COMP_0_BASE;
    trigger_reg=0;        //trigger module OFF (activate after DMA initialised)
    trigger_reg=500;     //set trigger level
    trigger_reg=300;     //set max monostable (ie: how long signal has to be stable ABOVE the trigger threshold before triggering)
    //declare write address in SDRAM, descriptor memory base address, DMA control register and fill context structure for ISR
    alt_u32 * sdram_write_addr = (alt_u32 *) SDRAM_CONTROLLER_BASE;
    alt_sgdma_descriptor *desc = (alt_sgdma_descriptor *) DESCRIPTOR_MEMORY_0_BASE  ;
    alt_sgdma_descriptor *next = (alt_sgdma_descriptor *) DESCRIPTOR_MEMORY_0_BASE  + sizeof(alt_sgdma_descriptor);
    alt_u32 sgdma_ctrl_reg = ALTERA_AVALON_SGDMA_CONTROL_IE_CHAIN_COMPLETED_MSK | ALTERA_AVALON_SGDMA_CONTROL_IE_GLOBAL_MSK ;
    t_MyContext MyContext;
    MyContext.cnt = 0;
    MyContext.write_addr = 0;
    MyContext.debug1 = 0;
    MyContext.debug2 = 0;
    MyContext.debug3 = 0;
    //begin program
    printf("Hello from Nios II!\n\n");
    //init lcd display
    lcd_init();
    char text  = " -Hello World!- ";
    lcd_print(text);
    usleep(1500000);
    //init RS232 serial port
    alt_up_rs232_dev *rspoint;
    rspoint=alt_up_rs232_open_dev("/dev/rs232_0");
    //init DMA
    alt_sgdma_dev *SGDMA = alt_avalon_sgdma_open("/dev/Trig_DMA");
    MyContext.pSGDMA=SGDMA;
    if (SGDMA == NULL){
        printf("Could not open DMA.\n\n");
    } else {
        printf("DMA initialised!\n\n");
        //build DMA descriptors
        alt_avalon_sgdma_construct_stream_to_mem_desc(desc,next,sdram_write_addr,NUM_BYTES_TO_DMA,0);
        int descriptor_status = alt_avalon_sgdma_check_descriptor_status(desc);
        if (descriptor_status == 0){
            printf("DMA descriptor generated successfully!\n\n");
        } else {
            printf("DMA descriptor generation failed (error code: %d). Look up in 'errno.h'.\n\n", descriptor_status);
        }
        //point DMA to ISR function
        alt_avalon_sgdma_register_callback(SGDMA,isr_sgdma_routine_ptr,sgdma_ctrl_reg, &MyContext);
        //start DMA
        int *start_dma = alt_avalon_sgdma_do_async_transfer(SGDMA,desc);
        if (start_dma == 0){
            printf("DMA started!\n\n");
        } else {
            printf("Error starting DMA (code: %d)\n\n", start_dma);
        }
    }
    //register ISR
    alt_irq_register(TRIG_DMA_IRQ, &MyContext, isr_sgdma_routine_ptr);
    //activate trigger module and streaming from ADC here
    trigger_reg=1;
    //adc_test(ADC_IF_0_BASE);
    //rs232_test(rspoint);
    //trigger_status_test(TEST_COMP_0_BASE);
   
    //sit in loop waiting for callback
    while(1){
        trigger_status_test(TEST_COMP_0_BASE);
        if (MyContext.cnt > 0)
        {
        printf("Number of interrupts: %d\n\n", MyContext.cnt);
        trigger_status_test(TEST_COMP_0_BASE);
        }
        //stops DMA in case of overflow
        if (MyContext.cnt > 64)
        {
            printf("...Overflow...\n");
            //stop SGDMA
            alt_avalon_sgdma_stop(MyContext.pSGDMA);
            //STOP trig
            trigger_reg=0;
            printf("Trig-on : %d\n",trigger_reg);
            usleep(400*1000); // debounce
        }
    }
    return 0;
}

I'm working with Quartus 14.1, Nios II EDS 14.1, on Windows 7 (64 bit).

-N

5 Replies

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

    Ok update on the problem - I've tried to write some values (with the cpu) into the SDRAM to check if the SGDMA actually accesses the memory at all. When I try to read these values, I get sent to the exception stack. I have a feeling that the SGDMA is causing problems with the memory (both on and off chip).

    How should I connect the SDRAM to the SGDMA to avoid this problem and successfully write to memory?
  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    UPDATE: The ISR executes correctly now, the CPU instructions were being overwritten in the SDRAM by the SGDMA, so I moved them to on chip memory.

    Unfortunately, when I check what's being written to the SDRAM, I only get 84 bytes of coherent data at the address 0x0 (ie: write_addr), before the values -1 and 0 are written for 32kb (ie: the specified write length), followed by 40 bytes of cohenrent data.
  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    This is in burst mode. In continuous mode, the first 32 bit word of data coming from my trigger component keeps getting rewritten. As far as I can tell, this is because the avstream_ready bit toggles for each word the SGDMA writes to memory (which resets my trigger device) - is there any way to keep this bit constant?

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

    I'm tempted to say save yourself the hassle and write your own DMA controller. I got sick of trying to coax any of the DMA controllers in Qsys to work correctly, they either worked intermittently, not at all, or corrupted the data. Its only about a days work to write a decent DMA controller and at least if it goes wrong you wrote the source code so it's easier to fix/debug.

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

    I actually just added two FIFOs (my data is 32 bits wide, the internal FIFO on the SGDMA couldn't cope - it was only storing two words, and then writing), which solved the problem. I used one for data (4096 bits deep) and one as a clock bridge (my ADC is clocked to 25MHz, and my DMA and SDRAM run at 100MHz).