Cyclone V HPS - Enabling DMA-330 FPGA interrupt requests

Hello,

I am currently trying to enable FPGA peripheral interrupt requests for DMA-330 controller inside Cyclone V HPS accessing On-Chip RAM through the h2f AXI Master bus but with no luck.

As a base for the project I use Test_DMA_PL330_LKM - the code for building DMA programs and starting transactions works fine even from Linux userpace together with u-dma-buf kernel driver for allocating DMAble buffers.

For example, this function works fine for data transfers with data lengths equal to multiplies of 128B:

ALT_STATUS_CODE pl330_read(void *dma_transfer_src_h, void *dma_transfer_dst_h, size_t len)
{
    ALT_DMA_CHANNEL_STATE_t channel_state;
    ALT_DMA_CHANNEL_FAULT_t fault;
    int error_count = 0;

    // If the size is zero, and no event is requested, just return success.//
    if ((len == 0))
    {
        return ALT_E_SUCCESS;
    }

    // For simplicity, we want len as a multiple of 128 B
    if ((len % 128) != 0) {
        printf("Error: Transfer size must be a multiple of 128 B!\r\n");
        return ALT_E_ERROR;
    }

    ALT_DMA_PROGRAM_t* program = (ALT_DMA_PROGRAM_t *)DMA_PROG_RD_V;

    if (alt_dma_program_init(program) != ALT_E_SUCCESS)
        return prog_err("Error at alt_dma_program_init - clearing program..\r\n");

    // Set source DMA transfer address
    if (alt_dma_program_DMAMOV(program, ALT_DMA_PROGRAM_REG_SAR, (uintptr_t)dma_transfer_src_h) != ALT_E_SUCCESS)
        return prog_err("Error at alt_dma_program_DMAMOV to SAR - clearing program..\r\n");

    // Set destination DMA transfer address
    if (alt_dma_program_DMAMOV(program, ALT_DMA_PROGRAM_REG_DAR, (uintptr_t)dma_transfer_dst_h) != ALT_E_SUCCESS)
        return prog_err("Error at alt_dma_program_DMAMOV to DAR - clearing program..\r\n");

    // Setup Config register for burst length of 16 transactions, each transaction is 64-bit (8B) => 128 B in total (largest possible config)
    uint32_t CCR_CFG = ALT_DMA_CCR_OPT_SB16 | ALT_DMA_CCR_OPT_SS64 | ALT_DMA_CCR_OPT_SA_DEFAULT | ALT_DMA_CCR_OPT_SP_DEFAULT | ALT_DMA_CCR_OPT_SC(7) | ALT_DMA_CCR_OPT_DB16 | ALT_DMA_CCR_OPT_DS64 | ALT_DMA_CCR_OPT_DA_DEFAULT | ALT_DMA_CCR_OPT_DP_DEFAULT | ALT_DMA_CCR_OPT_DC(7) | ALT_DMA_CCR_OPT_ES_DEFAULT;
    if (alt_dma_program_DMAMOV(program, ALT_DMA_PROGRAM_REG_CCR, CCR_CFG) != ALT_E_SUCCESS)
        return prog_err("Error at alt_dma_program_DMAMOV to CCR - clearing program..\r\n");

    // Add loop for bursts
    uint32_t loopcount = len / 128;
    if (loopcount > 1) {
        if (alt_dma_program_DMALP(program, loopcount) != ALT_E_SUCCESS)
            return prog_err("Error at alt_dma_program_DMALP - clearing program..\r\n");
    }
  
    // Add Load instruction
    if (alt_dma_program_DMALD(program, ALT_DMA_PROGRAM_INST_MOD_NONE) != ALT_E_SUCCESS)
        return prog_err("Error at alt_dma_program_DMALD - clearing program..\r\n");

    // Add Store instruction
    if (alt_dma_program_DMAST(program, ALT_DMA_PROGRAM_INST_MOD_NONE) != ALT_E_SUCCESS)
        return prog_err("Error at alt_dma_program_DMAST - clearing program..\r\n");

    // Add Loop End instruction
    if (loopcount > 1) {
        if (alt_dma_program_DMALPEND(program, ALT_DMA_PROGRAM_INST_MOD_NONE) != ALT_E_SUCCESS)
            return prog_err("Error at alt_dma_program_DMALPEND - clearing program..\r\n");
    }
  
    // Now that everything is done, end the program.
    if (alt_dma_program_DMAEND(program) != ALT_E_SUCCESS)
        return prog_err("Error at alt_dma_program_DMAEND - clearing program..\r\n");

    // Execute the program on the given channel.
    if (alt_dma_channel_exec(Dma_Channel, (ALT_DMA_PROGRAM_t *)DMA_PROG_RD_H) != ALT_E_SUCCESS)
        return prog_err("Error at alt_dma_channel_exec - clearing program..\r\n");

    return ALT_E_SUCCESS;
}

But I am not sure how to add the DMAFLUSHP, DMAWFP and the other instructions to make it work with the peripheral requests from the FPGA. When I tried to add DMAFLUSHP and DMAWFP with ALT_DMA_PERIPH_FPGA_0 before the loop, changed ALT_DMA_PROGRAM_INST_MOD_NONE to ALT_DMA_PROGRAM_INST_MOD_BURST and modified the last store instruction in the loop to DMASTP with ALT_DMA_PERIPH_FPGA_0 , the DMA channel state is stuck at ALT_DMA_CHANNEL_STATE_WFP when I toggle the f2h_dma_req0_burst signal inside the FPGA.

In other words, when the DMA program looks like this, it remains stuck waiting for the peripheral signal:

DMAMOV SAR, <source physical 32b addr>
DMAMOV DAR, <destination physical 32b addr>
DMAMOV CCR, <config 16 trans., 64b each, RO cache, defaults>
DMAFLUSHP P0
DMAWFP P0, burst
DMALP <num_loops_of_128B_trans - 1>
  DMALDB
  DMASTB
DMALPENDB
DMALDB
DMASTPB P0
DMAEND

(In my understanding the P0 peripheral is the ALT_DMA_PERIPH_FPGA_0, ie. f2h_dma_req0 signal in the fabric.)

I am also clearing all 8 bits in the RSTMGR-PER2MODRST register during boot to enable FPGA request interfaces.
Is there any other register I should modify to make it work? How should the DMA program look like for memory-to-memory operation with FPGA requests?

Thanks for any help.

Hi,

I am also stuck at the same point as you mentioned that is peripheral request from FPGA does not reach to DMAWFP instruction. Did you manage to solve this issue?

I am also using Altera DMA lib but in baremetal on cyclone V.

Any help will be appreciated.

Hi,

I didn’t get the DMAWFP instruction working, the Intel employee on their forum couldn’t provide any helpful information (you can try to revive the thread there). I though it could have been caused by the newest version of the Linux kernel I used, but now I guess it is not the case if it doesn’t work for you in baremetal either.

I guess one way to overcome this problem is to use the FPGA-to-HPS interrupts which I managed to get working using this Linux kernel module. The Altera DMA library could be simply added to that kernel module (similarly as in Test_DMA_PL330_LKM) so it would execute a prepared DMA microprogram in the memory when an interrupt from the FPGA is received (but it also means unecessary additional load for the processor, in comparison to the DMAWFP instruction which is handled by the DMA-330 controller itself). I don’t know how to register the FPGA interrupts in baremetal application though.

Hope this helps.