基于DW_axi_dmac_databook,对multi block transfer进行了验证。
Programming Flow for LLI-Based Multi-Block Transfer | ||
ARC Status | Software Sequence | postscript |
initial | 1)配置DMAC_CFGREG[1:0]为2'b11,对DMA进行使能; 2)配置CHx_CFG2[3:0]为4'b1111,表明是基于LLI的multi block传输; 3)基于传输类型(M2M/M2P/P2M/P2P)配置CHx_CFG2[34:32]; | |
LLI configuration | 1)配置CHx_SAR和CHx_DAR的地址; 2)配置CHx_BLOCK_TS来指定一个block包含位宽为SRC_TR_WIDTH数据的个数; 3)配置CHx_CTL[10:8].SRC_TR_WIDTH来表明每个数据的位宽; 4)配置CHx_CTL[47].AWLEN_EN,对AWLEN进行使能; 5)配置CHx_CTL[55:48].AWLEN,用于指定每个burst transaction中的数据个数(用于匹配destination的位宽); 6)配置CHx_CTL[58].IOC_BlkTfr为1,支持输出block传输结束的中断信号; 7)配置CHx_CTL[63].ShadowReg_Or_LLI_Valid为1,表明可以将有关寄存器的值写入shadowreg; 8)配置CHENREG2,拉高用于传输的通道使能和写使能,开始传输数据; 9)对CHx_CTL[63].ShadowReg_Or_LLI_Valid进行轮询,直到该bit值为0; | CHx_CTL必须是最后一个配置的寄存器 |
remaining block | 1)配置CHx_SAR/CHx_DAR/CHx_BLOCK_TS/CHx_CTL寄存器; 2)开始数据传输; | CHx_CTL必须是最后一个配置的寄存器 |
last block | 1)配置CHx_SAR/CHx_DAR/CHx_BLOCK_TS/CHx_CTL寄存器,并将CHx_CTL[62].SHADOWREG_OR_LLI_LAST信号拉高,表明这是最后一个block; 2)轮询CHx_INTSTATUS[1].DMA_TFR_DONE_IntStat的值直到该bit为1; 3)结束传输。 |
Programming Flow for Shadow-Register-Based Multi-Block Transfer | ||
ARC Status | Software Sequence | postscript |
initial | 1) Software reads the DMAC channel enable register (DMAC_ChEnReg) to select an available (unused) channel. 2) Software programs the CHx_CFG register with appropriate values for the DMA transfer. | 1) The SRC_MLTBLK_TYPE and/or DST_MLTBLK_TYPE bits must be set to 2’b10. 2) The CHx_CFG register must be programmed before programming the CHx_SAR, CHx_DAR, CHx_BLOCK_TS, or CHx_CTL registers. 3) If the slave interface data bus width or transfer size is less than 64 bits, CHx_CFG[7:0] should be updated in the first write to the CHx_CFG register. |
first block | 1) Software programs the CHx_SAR and/or CHx_DAR, CHx_BLOCK_TS, and CHx_CTL registers with appropriate values for the first block. DW_axi_dmac loads the corresponding shadow registers with these values. 2) Software enables the channel by writing 1 to the appropriate bit location in the DMAC_ChEnReg register. 3) DW_axi_dmac initiates the DMA block transfer operation based on the settings for the block transfer. 4) Software polls the ShadowReg_Or_LLI_Valid bit in the CHx_CTL register(VALID2) till it is 0. | 1) The CHx_CTL register must be the last register to be programmed with the ShadowReg_Or_LLI_Valid bit set to 1 to indicate that the shadow register contents are valid. If the slave interface data bus width or transfer size is less than 64 bits, CHx_CTL[63:56] must be updated last. 2)The block transfer might start immediately or after the hardware or software handshaking request, depending on the value of the TT_FC field in the CHx_CFG register. 3) If VALID1 is seen as '0', DW_axi_dmac waits till software writes (any value) to CHx_BLK_TFR_ResumeReqReg to indicate valid LLI availability, before attempting another Shadow Register fetch operation. DW_axi_dmac might generate 'ShadowReg_Or_LLI_Invalid_ERR' Interrupt in this case. 4) Else if VALID1 is seen as '1', DW_axi_dmac copies the shadow register contents to the registers used for executing the DMA block transfer (CHx_SAR and/or CHx_DAR, CHx_BLOCK_TS and CHx_CTL registers) and clears the VALID1&VALID2 to 0. 5) When CHx_CTL.ShadowReg_Or_LLI_Last is asserted, it means that the current block is the final block in the transfer.Otherwise it indicates that there are one or more blocks to be transferred and checks VALID1 bit again at the end of current block transfer. 6) DW_axi_dmac clears VALID2 to 0 only after copying the shadow register contents to the registers used for executing the DMA block transfer. 7) Software must program the shadow registers with a new set of values only after the VALID2 is set to 0. 8) If software tries to programs the shadow registers when the VALID2 is set to 1, DW_axi_dmac ignores this write operation, sets the SLVIF_ShadowReg_WrOnValid_ERR bit of the CHx_IntStatusReg register to 1, and generates an interrupt (if the corresponding interrupt generation is not masked off). |
remaining block | 1) Software programs the CHx_SAR and/or CHx_DAR, CHx_BLOCK_TS, and CHx_CTL registers with appropriate values for the next block. 2) DW_axi_dmac initiates the DMA block transfer operation based on the settings for the block transfer. 3) Software waits for the block transfer completion interrupt or polls the block transfer completion indication bit (BLOCK_TFR_DONE) of the CHx_IntStatusReg register until it is set to 1. 4) If there are one or more blocks to be transferred, software polls CHx_CTL.ShadowReg_Or_LLI_Valid(VALID2) bit until it is seen as 0 and go to step 1. | 1) The DMA block transfer corresponding to the previous shadow register contents may be in progress when software is programming these registers. 2) One read operation is enough as DW_axi_dmac should have already copied the shadow register contents and cleared VALID2 to 0. |
`ifndef NPC_RSU_XDMA_TCM2L3_SINGLE_BLOCK_TEST__SV
`define NPC_RSU_XDMA_TCM2L3_SINGLE_BLOCK_TEST__SV
class npc_rsu_xdma_tcm2l3_single_block_test extends npc_base_test;
`uvm_component_utils_begin(npc_rsu_xdma_tcm2l3_single_block_test)
`uvm_component_utils_end
function new(string name, uvm_component parent);
super.new(name,parent);
endfunction : new
// build_phase
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
uvm_config_db#(uvm_object_wrapper)::set(this, "npc_sys_env_h.npc_env_h.npc_noc_env_h.axi_env.slave[36].sequencer.run_phase", "default_sequence", axi_slave_mem_response_sequence::type_id::get());
uvm_config_db#(uvm_object_wrapper)::set(this, "npc_sys_env_h.npc_env_h.npc_noc_env_h.axi_env.slave[37].sequencer.run_phase", "default_sequence", axi_slave_mem_response_sequence::type_id::get());
endfunction : build_phase
task main_phase(uvm_phase phase);
bit [2047:0] l3_data_tmp;
bit [127:0] tcm_data_tmp;
bit [127:0] tcm_data_tmp1;
bit [127:0] tcm_data_q[$];
bit [63:0] rdata;
bit [63:0] wdata;
response_status_type rsp_status;
super.main_phase(phase);
`uvm_info("[NPC_BASE_TEST]: ","entering main_phase...", UVM_LOW)
phase.raise_objection(this);
`uvm_info(get_full_name(),$sformatf("debug info, round 1, now update l3 mem"), UVM_LOW)
tcm_data_q.delete();
// Fill 256Byte
for(int i=0; i<256; i++)begin
l3_data_tmp[8*i+:8] = i;
end
npc_sys_env_h.npc_env_h.npc_noc_env_h.axi_env.slave[36].axi_slave_mem.write(32'h4000_0000, l3_data_tmp[1023:0]);
npc_sys_env_h.npc_env_h.npc_noc_env_h.axi_env.slave[36].axi_slave_mem.write(32'h4000_0080, l3_data_tmp[2047:1024]);
for(int j=0; j<16; j++)begin
tcm_data_tmp = '0;
tcm_data_tmp = l3_data_tmp[j*128+:128];
tcm_data_q.push_back(tcm_data_tmp);
end
//for(int i=0; i<(1024*4/128); i++)begin
// std::randomize(l3_data_tmp) with{l3_data_tmp dist{ 0:/5, [1:'1-1]:/90,'1:/5};};
// npc_sys_env_h.npc_env_h.npc_noc_env_h.axi_env.slave[36].axi_slave_mem.write(32'h4000_0000+i*128,l3_data_tmp);
// for(int j=0; j<8; j++)begin
// tcm_data_tmp = '0;
// npc_sys_env_h.npc_env_h.npc_noc_env_h.axi_env.slave[47].axi_slave_mem.write(i*128+j*16,tcm_data_tmp);
// tcm_data_tmp = l3_data_tmp[j*128+:128];
// tcm_data_q.push_back(tcm_data_tmp);
// end
//end
`uvm_info(get_full_name(),$sformatf("debug info, round 2, now config rsu.xdma"), UVM_LOW)
m_reg_seq.reg_base_seq.get_handle();
m_reg_seq.reg_write_obj(`RSU_RAL.ss_rstnctrl, 32'hffff_ffff, DV_FRONTDOOR, rsp_status);
m_reg_seq.reg_read_obj(`RSU_DMAC_COMMON_REG.DMAC_IDREG, rdata, DV_FRONTDOOR, rsp_status);
`uvm_info(get_full_name(),$sformatf("debug info, dmac id is 'h%0h", rdata), UVM_LOW)
m_reg_seq.reg_read_obj(`RSU_DMAC_COMMON_REG.DMAC_CFGREG, rdata, DV_FRONTDOOR, rsp_status);
wdata = rdata;
// Enable INT and DMAC
wdata[1:0] = 2'b11;
m_reg_seq.reg_write_obj(`RSU_DMAC_COMMON_REG.DMAC_CFGREG, wdata, DV_FRONTDOOR, rsp_status);
m_reg_seq.reg_read_obj(`RSU_DMAC_COMMON_REG.DMAC_CFGREG, rdata, DV_FRONTDOOR, rsp_status);
m_reg_seq.reg_read_obj(`RSU_DMAC_COMMON_REG.DMAC_CHENREG2, rdata, DV_FRONTDOOR, rsp_status);
if(rdata[1] != 0) begin
`uvm_error(get_full_name(),$sformatf("debug info, ch2 is not free"))
end
else begin
/*
* CFG2
*/
`uvm_info(get_full_name(),$sformatf("debug info, ch2 is free, we will use it"), UVM_LOW)
m_reg_seq.reg_read_obj(`RSU_DMAC_CHANNEL2_REG.CH2_CFG2, rdata, DV_FRONTDOOR, rsp_status);
wdata = rdata;
// SRC_MULTBLK_TYPE
wdata[1:0] = 2'b10;
// DST_MULTBLK_TYPE
wdata[3:2] = 2'b10;
// TT_FC
wdata[34:32] = 3'h0;
// LOCK_CH_L
wdata[54:53] = 2'b01;
m_reg_seq.reg_write_obj(`RSU_DMAC_CHANNEL2_REG.CH2_CFG2, wdata, DV_FRONTDOOR, rsp_status);
/*
* For first block
*/
// SAR - DDR
wdata = 64'h4000_0000;
m_reg_seq.reg_write_obj(`RSU_DMAC_CHANNEL2_REG.CH2_SAR, wdata, DV_FRONTDOOR, rsp_status);
m_reg_seq.reg_read_obj(`RSU_DMAC_CHANNEL2_REG.CH2_SAR, rdata, DV_FRONTDOOR, rsp_status);
`uvm_info(get_full_name(),$sformatf("debug info, ch2 sar is 'h%0h", rdata), UVM_LOW)
// DAR - TCM
wdata = 64'h0;
m_reg_seq.reg_write_obj(`RSU_DMAC_CHANNEL2_REG.CH2_DAR, wdata, DV_FRONTDOOR, rsp_status);
// BLOCK_TS
wdata = 31; // BLOCK_TS
m_reg_seq.reg_write_obj(`RSU_DMAC_CHANNEL2_REG.CH2_BLOCK_TS, wdata, DV_FRONTDOOR, rsp_status);
// CH2_CTL
m_reg_seq.reg_read_obj(`RSU_DMAC_CHANNEL2_REG.CH2_CTL, rdata, DV_FRONTDOOR, rsp_status);
wdata = rdata;
// SHADOWREG_OR_LLI_VALID
wdata[63] = 1'b1;
// IOC_BlkTfr
wdata[58] = 1'b1;
// AWLEN
wdata[55:48] = 8'h3;
// AWLEN_EN
wdata[47] = 1'b1;
// SRC_TR_WIDTH
wdata[10:8] = 3'h2;
`uvm_info(get_full_name(),$sformatf("debug info, round 4, now config ch2_cfg register, wdata is 'h%0h",wdata), UVM_LOW)
m_reg_seq.reg_write_obj(`RSU_DMAC_CHANNEL2_REG.CH2_CTL, wdata, DV_FRONTDOOR, rsp_status);
// CHENREG2
m_reg_seq.reg_read_obj(`RSU_DMAC_COMMON_REG.DMAC_CHENREG2, rdata, DV_FRONTDOOR, rsp_status);
wdata = rdata;
// CH2_EN
wdata[1] = 1'b1;
// CH2_WE
wdata[17] = 1'b1;
m_reg_seq.reg_write_obj(`RSU_DMAC_COMMON_REG.DMAC_CHENREG2, wdata, DV_FRONTDOOR, rsp_status);
`uvm_info(get_full_name(),$sformatf("debug info, round 4, now read chenreg2, rdata is 'h%0h",rdata), UVM_LOW)
end
/*
* Polling shadow register valid
*/
`uvm_info(get_full_name(),$sformatf("debug info, round 4, now poll rsu.xdma shadowreg valid"), UVM_LOW)
rdata[63] = 1;
while (rdata[63] == 1'b1) begin
m_reg_seq.reg_read_obj(`RSU_DMAC_CHANNEL2_REG.CH2_CTL, rdata, DV_FRONTDOOR, rsp_status);
`uvm_info(get_full_name(),$sformatf("debug info, round 4, now read ch2_ctl, rdata is 'h%0h", rdata), UVM_LOW)
#100ns;
end
/*
* For second block (last)
*/
// SAR - DDR
wdata = 64'h4000_0080;
m_reg_seq.reg_write_obj(`RSU_DMAC_CHANNEL2_REG.CH2_SAR, wdata, DV_FRONTDOOR, rsp_status);
m_reg_seq.reg_read_obj(`RSU_DMAC_CHANNEL2_REG.CH2_SAR, rdata, DV_FRONTDOOR, rsp_status);
`uvm_info(get_full_name(),$sformatf("debug info, ch2 sar is 'h%0h", rdata), UVM_LOW)
// DAR - TCM
wdata = 64'h80;
m_reg_seq.reg_write_obj(`RSU_DMAC_CHANNEL2_REG.CH2_DAR, wdata, DV_FRONTDOOR, rsp_status);
// CH2_CTL
m_reg_seq.reg_read_obj(`RSU_DMAC_CHANNEL2_REG.CH2_CTL, rdata, DV_FRONTDOOR, rsp_status);
wdata = rdata;
// SHADOWREG_OR_LLI_VALID
wdata[63] = 1'b1;
// SHADOWREG_OR_LLI_LAST
wdata[62] = 1'b1;
m_reg_seq.reg_write_obj(`RSU_DMAC_CHANNEL2_REG.CH2_CTL, wdata, DV_FRONTDOOR, rsp_status);
`uvm_info(get_full_name(),$sformatf("debug info, round 4, write CH2 shadowreg"), UVM_LOW)
rdata = 0;
while(!rdata[1])begin
m_reg_seq.reg_read_obj(`RSU_DMAC_CHANNEL2_REG.CH2_INTSTATUS, rdata, DV_FRONTDOOR, rsp_status );
`uvm_info(get_full_name(),$sformatf("debug info, round 4, now read ch2_intstatus, rdata is 'h%0h", rdata), UVM_LOW)
#100ns;
end
`uvm_info(get_full_name(),$sformatf("debug info, round 4, now check data l3 vs tcm"), UVM_LOW)
for(int i=0; i<16 ; i++)begin
tcm_data_tmp = npc_sys_env_h.npc_env_h.npc_noc_env_h.axi_env.slave[47].axi_slave_mem.read(i*16);
tcm_data_tmp1 = tcm_data_q.pop_front();
if( tcm_data_tmp == tcm_data_tmp1 )
`uvm_info(get_full_name(),$sformatf("debug info, tcm data check ok , addr is %0d", i*16), UVM_LOW)
else
`uvm_error(get_full_name(),$sformatf("debug info, tcm data check fail , addr is %0d", i*16))
end
`uvm_info(get_full_name(),$sformatf("debug info, rsu xdma single bolck transfer test end"), UVM_LOW)
phase.drop_objection(this);
`uvm_info("[NPC_BASE_TEST]: ","exiting main_phase...", UVM_LOW)
endtask : main_phase
endclass : npc_rsu_xdma_tcm2l3_single_block_test
`endif