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.