DMAC and multi-page buffering - FMCOMMS3/3 ref design

Memo ADI multi-page adc

Hi,

I am in the process of implementing a multi-page ADC buffer approach, using the FMCOMMS ref. design, which uses the DMAC core.

For now, I am using a ‘polled’ approach, but I intend to move to the interrupt-based approach later (using the adc interrupt example code).

However, I see an inexplicable behaviour (for me, at least J ) with the ‘polling’ code. In short, if I read the TRANSFER_ID (0x404) register  before the TRANSFER_DONE ‘while polling’ loop, the programs hangs there, since the TRANSFER_DONE bits register stays at value 0x1 (ie transfer ID 0 is checked for). If I don’t read the TRANSFER_ID  register, the program goes through the TRANSFER_DONE polling loop. In other words, even though the ACTIVE_TRANSFER_ID register is 1, it looks like the code is polling for transfer ID 0 to be finished. If I read the TRANSFER_ID register prior in the code (as mentioned above), the code thus hangs.

I am including the code here (but removed the interrupt example section).

I also included a printing of results.

Note that the DMAC core version is 0x040062.

+++++ code +++

                adc_dma_write(AXI_DMAC_REG_CTRL, 0x0);

                adc_dma_write(AXI_DMAC_REG_CTRL, AXI_DMAC_CTRL_ENABLE);

 

                adc_dma_write(AXI_DMAC_REG_IRQ_MASK, 0x0);

 

                adc_dma_read(AXI_DMAC_REG_TRANSFER_ID, &transfer_id);

                // lb add

                               adc_dma_read(0, &reg_val);

                               xil_printf("DMAC version %x \n\r", reg_val );

 

                               adc_dma_read(AXI_DMAC_REG_ACTIVE_TRANSFER_ID, &active_transfer_id);

                               adc_dma_read(AXI_DMAC_REG_TRANSFER_DONE, &done_transfer_id_bits);

                               xil_printf("begining: next transfer ID: %x active tr ID: %x  done tr ID bits: %x\n\r", transfer_id,active_transfer_id, done_transfer_id_bits);

                // end lb add

 

                adc_dma_read(AXI_DMAC_REG_IRQ_PENDING, &reg_val);

                adc_dma_write(AXI_DMAC_REG_IRQ_PENDING, reg_val);

 

#ifdef ADC_DMA_IRQ_EXAMPLE

.... (IRQ example code removed)

#else

                adc_dma_write(AXI_DMAC_REG_DEST_ADDRESS, start_address);

                adc_dma_write(AXI_DMAC_REG_DEST_STRIDE, 0x0);

                adc_dma_write(AXI_DMAC_REG_X_LENGTH, length - 1);

                adc_dma_write(AXI_DMAC_REG_Y_LENGTH, 0x0);

 

                adc_dma_write(AXI_DMAC_REG_START_TRANSFER, 0x1);

                /* Wait until the new transfer is queued. */

                do {

                               adc_dma_read(AXI_DMAC_REG_START_TRANSFER, &reg_val);

                }

                while(reg_val == 1);

 

                //lb add

//  mdelay(10);  // add some delay to see

 

//            adc_dma_read(AXI_DMAC_REG_TRANSFER_ID, &transfer_id);  // strange: reading the reg_tranfer id clears or stop the transfer !!!

                adc_dma_read(AXI_DMAC_REG_ACTIVE_TRANSFER_ID, &active_transfer_id);

                adc_dma_read(AXI_DMAC_REG_TRANSFER_DONE, &done_transfer_id_bits);

                xil_printf("post queueing : next transfer ID: %x active tr ID: %x  done tr ID bits: %x\n\r", transfer_id,active_transfer_id, done_transfer_id_bits);

 

                mdelay(100);  // add some delay to see, no effect

                //end lb add

 

                /* Wait until the current transfer is completed. */

                do {

                               adc_dma_read(AXI_DMAC_REG_IRQ_PENDING, &reg_val);

                }

                while(reg_val != (IRQ_TRANSFER_QUEUED | IRQ_TRANSFER_COMPLETED));

                adc_dma_write(AXI_DMAC_REG_IRQ_PENDING, reg_val);

 

                // lb add

                mdelay(300);  // add some delay to see, no effect

 

//            adc_dma_read(AXI_DMAC_REG_TRANSFER_ID, &transfer_id); // strange: reading the reg_tranfer id clears or stop the transfer !!!

                adc_dma_read(AXI_DMAC_REG_ACTIVE_TRANSFER_ID, &active_transfer_id);

                adc_dma_read(AXI_DMAC_REG_TRANSFER_DONE, &done_transfer_id_bits);

                xil_printf("post waiting actual transfer done: next transfer ID: %x active tr ID: %x  done tr ID bits: %x\n\r", transfer_id,active_transfer_id, done_transfer_id_bits);

 

 

                /* Wait until the transfer with the ID transfer_id is completed. */

                do {

                               adc_dma_read(AXI_DMAC_REG_TRANSFER_DONE, &reg_val);

                }

                while((reg_val & (1 << transfer_id)) != (1 << transfer_id));

 

                // lb add

                adc_dma_read(AXI_DMAC_REG_TRANSFER_ID, &transfer_id);

                adc_dma_read(AXI_DMAC_REG_ACTIVE_TRANSFER_ID, &active_transfer_id);

                xil_printf("\n\rpost acquisition , transfer done ID bits %x , next transfer ID: %x active tr ID: %x\n\r ", reg_val, transfer_id,active_transfer_id );

 

+++ print out of values +++

DMAC version 40062

 

begining: next transfer ID: 0 active tr ID: 0  done tr ID bits: 0

 

post queueing : next transfer ID: 0 active tr ID: 0  done tr ID bits: 0 (note: next transfer ID not read)

 

post waiting actual transfer done: next transfer ID: 0 active tr ID: 1  done tr ID bits: 1 (note: next transfer ID not read)

 

post acquisition , transfer done ID bits 1 , next transfer ID: 1 active tr ID: 1

 

++++++

 

It is most probably timing-related, but cannot find why and a way around.  Or maybe it is a configuration parameter to the core itself. I also checked that the code was not in ‘CYCLIC’ mode.

I am using an FMCOMMS2 on a Zedboard, but I also checked on a ZCU104/FMCOMMS2 combination. Same behaviour.

 

I tried to queue up an additional buffer, but the same behaviour happens: the program seems to be lagging by a value of 1 on the transfer ID value (eg it checks to transfer ID minus one the active transfer ID; I made a patch to reflect this, but not sure if it is viable).

 

Also, is there some example code on how to implement a multi-page buffer ? One question I have is about the ‘modulo number of buffers’ behaviour. What happens after transfer ID 3 is done. Does the DMAC core switch automatically to transfer ID 0 (assuming there are 4 buffers, as there seems to be).

 

Thanks for the feedback,

 

BRs,

 

Louis