FPGA Intellectual Property
PCI Express*, Networking and Connectivity, Memory Interfaces, DSP IP, and Video IP

SGDMA Interrupt

Altera_Forum
Honored Contributor II
1,435 Views

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
0 Kudos
5 Replies
Altera_Forum
Honored Contributor II
617 Views

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?
0 Kudos
Altera_Forum
Honored Contributor II
617 Views

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.
0 Kudos
Altera_Forum
Honored Contributor II
617 Views

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?

0 Kudos
Altera_Forum
Honored Contributor II
617 Views

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.

0 Kudos
Altera_Forum
Honored Contributor II
617 Views

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).

0 Kudos
Reply