I have some working code that continuously takes in serial data to on-chip ram from 2 stream-to-memory sgdma's, and puts the serial data in a circular buffer in ddr memory.
The code puts the two stream-to-memory sgdma's in park mode, and the descriptor points back to itself for continuous operation. A call back is registered for the completion of each stream-to-memory descriptor to start the data transfer to the ddr3 circular buffer. The ddr3 transfer is done with a memory-to-memory sgdma with and single descriptor chain. The descriptor chain is rebuilt after each transfer.
Comments are more than welcome...
// CWstream1_DMA SGDMA callback function
// * After each descriptor is processed (1600 bytes), the data is transfered to DDR3 memory in a circular buffer
void CWstream1_callback_function(void * context)
{
// tx tells the circular buffer which sgdma was processed
tx=0;
alt_avalon_sgdma_do_async_transfer(DDR3_DMA, &DDR3_DMA_desc[0]);
}
// CWstream2_DMA SGDMA callback function
// * After each descriptor is processed (1600 bytes), the data is transfered to DDR3 memory in a circular buffer
void CWstream2_callback_function(void * context)
{
IOWR_ALTERA_AVALON_PIO_DATA(LED_PIO_BASE, 1);
// tx tells the circular buffer which sgdma was processed
tx=1;
alt_avalon_sgdma_do_async_transfer(DDR3_DMA, &DDR3_DMA_desc[0]);
}
// DDR3_DMA callback function
// * Re-initialize the descriptor for the next memory transfer
// * Check the memory position to see if the circular buffer needs to start over
void DDR3_callback_function(void * context)
{
// update the write address of the circular buffer
write_addr = write_addr + 0x640;
if (write_addr >= 0x3e80)
{
DDR3_write_addr = DDR3_BASE;
write_addr = 0;
}
else
DDR3_write_addr = DDR3_BASE + write_addr;
// Check which sgdma was processed so we can know where to read the data from
if (tx == 1)
{
alt_avalon_sgdma_construct_mem_to_mem_desc(&DDR3_DMA_desc[0], &DDR3_DMA_desc[1], CW_DAQ1_write_addr, DDR3_write_addr, 1600, 0, 0);
IOWR_ALTERA_AVALON_PIO_DATA(LED_PIO_BASE, 3);
IOWR_ALTERA_AVALON_PIO_DATA(LED_PIO_BASE, 4);
}
else
{
alt_avalon_sgdma_construct_mem_to_mem_desc(&DDR3_DMA_desc[0], &DDR3_DMA_desc[1], CW_DAQ2_write_addr, DDR3_write_addr, 1600, 0, 0);
IOWR_ALTERA_AVALON_PIO_DATA(LED_PIO_BASE, 2);
}
DDR3_DMA_desc[0].control=128;
DDR3_DMA_desc[1].control=0;
}
// Main code entry
// * Write a message to the LCD display for fun
// * Initialize pushbutton callbacks
// * Clear memory space
// * Start DMA engines
// * Sit in a while loop and wait for callbacks
int main(void)
{
// Initialize circular DDR3 transfer variables
write_addr = 0;
// This is a software reset to the ping-pong selector hardware counter.
// Pull this low and then return to the high state will reset the counter to 0.
IOWR_ALTERA_AVALON_PIO_DATA(PIO_PROGRAM_BASE, 0);
// Reset the sgdma controller
// This is a software reset, - not sure if needed
// The documentation states this is a last resort control, so we should remove this if we can.
// Embedded Peripherals IP User Guide, pg 25-12
IOWR_32DIRECT(SGDMA_ST1_BASE, 16, 0x10000);
IOWR_32DIRECT(SGDMA_ST2_BASE, 16, 0x10000);
usleep(1000);
IOWR_32DIRECT(SGDMA_ST1_BASE, 16, 0x10000);
IOWR_32DIRECT(SGDMA_ST2_BASE, 16, 0x10000);
usleep(1000);
// Open the CW streaming scatter-gather DMA controllers
CWstream_DMA1 = alt_avalon_sgdma_open("/dev/sgdma_st1");
if(CWstream_DMA1 == NULL)
printf("Could not open the CW SG-DMA1\n");
CWstream_DMA2 = alt_avalon_sgdma_open("/dev/sgdma_st2");
if(CWstream_DMA2 == NULL)
printf("Could not open the CW SG-DMA2\n");
// Open the Memory transfer to circular buffer scatter-gather DMA controller
DDR3_DMA = alt_avalon_sgdma_open("/dev/sgdma_ddr3");
if(DDR3_DMA == NULL)
printf("Could not open the DDR3 SG-DMA\n");
// Set the write addresses for data transfers
CW_DAQ1_write_addr = (alt_u32 *)(DAQ1_MEM_BASE);
CW_DAQ2_write_addr = (alt_u32 *)(DAQ2_MEM_BASE);
DDR3_write_addr = (alt_u32 *)(DDR3_BASE);
// Set up the CW stream 1 descriptor
// The descriptor points back to itself for continuous operation
// The PARK bit of the control register must be set in order to re-use the descriptor (done in the callback registration)r
alt_avalon_sgdma_construct_stream_to_mem_desc(&CWstream_DMA1_desc[0], &CWstream_DMA1_desc[0], CW_DAQ1_write_addr, 1600, 0);
// Set the OWNED_BY_HW bit to 1 on desc[0]
CWstream_DMA1_desc[0].control=128;
// Set up the CW stream 2 descriptor
alt_avalon_sgdma_construct_stream_to_mem_desc(&CWstream_DMA2_desc[0], &CWstream_DMA2_desc[0], CW_DAQ2_write_addr, 1600, 0);
CWstream_DMA2_desc[0].control=128;
// Set up DDR3 1 descriptor
alt_avalon_sgdma_construct_mem_to_mem_desc(&DDR3_DMA_desc[0], &DDR3_DMA_desc[1], CW_DAQ1_write_addr, DDR3_write_addr, 1600, 0, 0);
// Set the OWNED_BY_HW bit to 1 on desc[0], and set the bit to 0 on desc[1]
// This stops the descriptor chain at desc[1] and flags an interrupt for its callback function
DDR3_DMA_desc[0].control=128;
DDR3_DMA_desc[1].control=0;
// Register callback functions
// CWstream1 callback
// The PARK_MASK allows prevent the OWNED_BY_HW bit from being cleared after the descriptor is processed
alt_avalon_sgdma_register_callback(CWstream_DMA1,
&CWstream1_callback_function,
(ALTERA_AVALON_SGDMA_CONTROL_IE_GLOBAL_MSK |
ALTERA_AVALON_SGDMA_CONTROL_PARK_MSK |
ALTERA_AVALON_SGDMA_CONTROL_IE_DESC_COMPLETED_MSK |
ALTERA_AVALON_SGDMA_CONTROL_IE_CHAIN_COMPLETED_MSK ),
NULL);
// CWstream2 callback
alt_avalon_sgdma_register_callback(CWstream_DMA2,
&CWstream2_callback_function,
(ALTERA_AVALON_SGDMA_CONTROL_IE_GLOBAL_MSK |
ALTERA_AVALON_SGDMA_CONTROL_PARK_MSK |
ALTERA_AVALON_SGDMA_CONTROL_IE_DESC_COMPLETED_MSK |
ALTERA_AVALON_SGDMA_CONTROL_IE_CHAIN_COMPLETED_MSK),
NULL);
//DDR3 callback
alt_avalon_sgdma_register_callback(DDR3_DMA,
&DDR3_callback_function,
(ALTERA_AVALON_SGDMA_CONTROL_IE_GLOBAL_MSK |
ALTERA_AVALON_SGDMA_CONTROL_IE_CHAIN_COMPLETED_MSK),
NULL);
usleep(1000);
IOWR_ALTERA_AVALON_PIO_DATA(PIO_PROGRAM_BASE, 1);
// Start the CW transfers
alt_avalon_sgdma_do_async_transfer(CWstream_DMA1, &CWstream_DMA1_desc[0]);
alt_avalon_sgdma_do_async_transfer(CWstream_DMA2, &CWstream_DMA2_desc[0]);
// Sit in a while loop while callbacks happen
while (1)
{
}
// Should not reach this code
printf("\nExit\n\n");
return 0;
}