はじめに
Xilinx ZynqMP SoC VIP の中を調べる(その5)
今回は、前回でてきた zynq_ultra_ps_e_vip_v1_0_12_apis.sv の中をもう少し見ていきます。
このファイルは、
data/ip/xilinx/zynq_ultra_ps_e_vip_v1_0
にあります。
read_mem/wirte_mem API
read_mem/write_mem APIは、次のようになっています。
task automatic read_mem; input [addr_width-1:0] start_addr; input [max_burst_bytes_width :0] no_of_bytes; output[max_burst_bits-1 :0] data; reg [1:0] mem_type; integer succ; begin mem_type = decode_address(start_addr); if(check_addr_aligned(start_addr)) begin case(mem_type) OCM_MEM : begin ocmc.ocm.read_mem(data,(start_addr-ocm_start_addr),no_of_bytes); if(DEBUG_INFO) $display("[%0d] : %0s : Starting Address(0x%0h) -> Read %0d bytes of data from OCM Memory ",$time, DISP_INFO, start_addr, no_of_bytes); end DDR_MEM : begin ddrc.ddr.read_mem(data,start_addr,no_of_bytes); if(DEBUG_INFO) $display("[%0d] : %0s : Starting Address(0x%0h) -> Read %0d bytes of data from DDR Memory",$time, DISP_INFO, start_addr, no_of_bytes); end default : begin $display("[%0d] : %0s : Address(0x%0h) is out-of-range. 'read_mem' call failed ...\n",$time, DISP_ERR, start_addr); if(STOP_ON_ERROR) $stop; end endcase end else begin $display("[%0d] : %0s : Address(0x%0h) has to be 32-bit aligned. 'read_mem' call failed ...",$time, DISP_ERR, start_addr); if(STOP_ON_ERROR) $stop; end end endtask /* API for backdoor write to memories (DDR/OCM) */ task automatic write_mem; input [max_burst_bits-1 :0] data; input [addr_width-1:0] start_addr; input [max_burst_bytes_width:0] no_of_bytes; reg [1:0] mem_type; integer succ; begin mem_type = decode_address(start_addr); if(check_addr_aligned(start_addr)) begin case(mem_type) OCM_MEM : begin ocmc.ocm.write_mem(data,(start_addr-ocm_start_addr),no_of_bytes,all_strb_valid); if(DEBUG_INFO) $display("[%0d] : %0s : Starting Address(0x%0h) -> Write %0d bytes of data to OCM Memory",$time, DISP_INFO, start_addr, no_of_bytes); end DDR_MEM : begin ddrc.ddr.write_mem(data,start_addr,no_of_bytes,all_strb_valid); if(DEBUG_INFO) $display("[%0d] : %0s : Starting Address(0x%0h) -> Write %0d bytes of data to DDR Memory",$time, DISP_INFO, start_addr, no_of_bytes); end default : begin $display("[%0d] : %0s : Address(0x%0h) is out-of-range. 'write_mem' call failed ...\n",$time, DISP_ERR, start_addr); if(STOP_ON_ERROR) $stop; end endcase end else begin $display("[%0d] : %0s : Address(0x%0h) has to be 32-bit aligned. 'write_mem' call failed ...",$time, DISP_ERR, start_addr); if(STOP_ON_ERROR) $stop; end end endtask
各引数のビット幅は、zynq_ultra_ps_e_vip_v1_0_12_local_params.sv の中で定義されています。
parameter addr_width = 40; // maximum address width parameter data_width = 32; // maximum data width. /* for internal read/write APIs used for data transfers */ parameter max_burst_len = 256; /// maximum brst length on axi parameter max_data_width = 128; // maximum data width for internal AXI bursts parameter max_burst_bits = (max_data_width * max_burst_len); // maximum data width for internal AXI bursts parameter max_burst_bytes = (max_burst_bits)/8; // maximum data bytes in each transfer parameter max_burst_bytes_width = clogb2(max_burst_bytes); // maximum data width for internal AXI bursts parameter all_strb_valid = 2048'hFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF;
テストベンチの mpsoc_tb.v では、アドレスは32ビット、データも32ビットです。省略した部分は0になるので問題はなさそうですが。
tb.mpsoc_sys.mpsoc_preset_i.zynq_ultra_ps_e_0.inst.write_data(32'hA0000000,4, 32'hFFFFFFFF, resp); #200 tb.mpsoc_sys.mpsoc_preset_i.zynq_ultra_ps_e_0.inst.read_data(32'hA0000000,4, read_data, resp); #200
read_mem/write_mem API では、decode_address でアドレスのチェックを行っています。このAPIもこのファイル内で定義されています。
OCM_MEM/DDR_MEM/REG_MEM のアドレス空間で分けています。それ以外の時は、read_mem/write_mem API の中でエラーを表示し、STOP_ON_ERROR が ON の時は、$stop システムタスクにてシミュレータを止めています。
read_mem/write_mem API では、OCM_MEM と DDR_MEM の空間のみOKで、REG_MEM の空間にはアクセスできません。REG_MEMの空間には、read_register API にてアクセスすることになるようです。
/* local API for address decoding*/ function automatic [1:0] decode_address; input [addr_width-1:0] address; begin if(address >= ocm_start_addr && address <= ocm_end_addr ) decode_address = OCM_MEM; /// OCM else if(address >= ddr_start_addr && address <= ddr_end_addr) decode_address = DDR_MEM; /// DDR else if(C_HIGH_DDR_EN && address >= high_ddr_start_addr) decode_address = DDR_MEM; /// DDR else if(address >= reg_start_addr && address <= reg_end_addr) decode_address = REG_MEM; /// Register Map else decode_address = INVALID_MEM_TYPE; /// ERROR in Address end endfunction
read_register API
REG_MEMの空間へは、read_register API でアクセスします。リードはこの read_regitser API でできますが、ライトはどうするのでしょうか?
/* API to read single register */ task read_register; input [addr_width-1:0] addr; output[data_width-1:0] data; begin if(check_addr_aligned(addr)) begin if(decode_address(addr) == REG_MEM) begin if(DEBUG_INFO) $display("[%0d] : %0s : Reading Register (0x%0h) ",$time, DISP_INFO, addr ); regc.regm.get_data(addr >> 2, data); if(DEBUG_INFO) $display("[%0d] : %0s : DONE -> Reading Register (0x%0h), Data returned(0x%0h)",$time, DISP_INFO, addr, data ); end else begin $display("[%0d] : %0s : Invalid Address(0x%0h) for Register Read. 'read_register' call failed ...",$time, DISP_ERR, addr); end end else begin data = 0; $display("[%0d] : %0s : Address(0x%0h) has to be 32-bit aligned. 'read_register' call failed ...",$time, DISP_ERR, addr); end end endtask
set_stop_on_error API
decode_address関数の中で、STOP_ON_ERROR という変数を使っていました。この変数は、set_stop_on_error API にて設定します。引数の LEVEL は 1ビットですので、STOP_ON_ERRORも1ビットです。
/* API for setting the STOP_ON_ERROR*/ task automatic set_stop_on_error; input LEVEL; begin $display("[%0d] : %0s : Setting Stop On Error as %0b",$time, DISP_INFO, LEVEL); STOP_ON_ERROR = LEVEL; // M_AXI_HPM0_FPD.master.set_stop_on_error(LEVEL); // M_AXI_HPM1_FPD.master.set_stop_on_error(LEVEL); // M_AXI_HPM0_LPD.master.set_stop_on_error(LEVEL); // S_AXI_HPC0_FPD.slave.set_stop_on_error(LEVEL); // S_AXI_HPC1_FPD.slave.set_stop_on_error(LEVEL); // S_AXI_HP0_FPD.slave.set_stop_on_error(LEVEL); // S_AXI_HP1_FPD.slave.set_stop_on_error(LEVEL); // S_AXI_HP2_FPD.slave.set_stop_on_error(LEVEL); // S_AXI_HP3_FPD.slave.set_stop_on_error(LEVEL); // S_AXI_HPM0_LPD.slave.set_stop_on_error(LEVEL); // S_AXI_ACP.slave.set_stop_on_error(LEVEL); // S_AXI_ACE.slave.set_stop_on_error(LEVEL); M_AXI_HPM0_FPD.STOP_ON_ERROR = LEVEL; M_AXI_HPM1_FPD.STOP_ON_ERROR = LEVEL; M_AXI_HPM0_LPD.STOP_ON_ERROR = LEVEL; S_AXI_HPC0_FPD.STOP_ON_ERROR = LEVEL; S_AXI_HPC1_FPD.STOP_ON_ERROR = LEVEL; S_AXI_HP0_FPD.STOP_ON_ERROR = LEVEL; S_AXI_HP1_FPD.STOP_ON_ERROR = LEVEL; S_AXI_HP2_FPD.STOP_ON_ERROR = LEVEL; S_AXI_HP3_FPD.STOP_ON_ERROR = LEVEL; S_AXI_HPM0_LPD.STOP_ON_ERROR = LEVEL; S_AXI_ACP.STOP_ON_ERROR = LEVEL; S_AXI_ACE.STOP_ON_ERROR = LEVEL; end endtask
por_srstv_reset / fpga_soft_reset API
この2つのAPIは、リセット関連のAPIです。VIP内の gen_rst インスタンスの同じAPIを呼び出しているだけです。
/* API for por and strb reset control */ task automatic por_srstb_reset; input por_reset_ctrl; begin if(DEBUG_INFO) $display("[%0d] : %0s : POR and STRB Reset called for 0x%0h",$time, DISP_INFO, por_reset_ctrl); // gen_rst.por_srstb_reset(por_reset_ctrl); gen_rst.por_srstb_reset(por_reset_ctrl); end endtask /* API for soft reset control */ task automatic fpga_soft_reset; input[data_width-1:0] reset_ctrl; begin if(DEBUG_INFO) $display("[%0d] : %0s : FPGA Soft Reset called for 0x%0h",$time, DISP_INFO, reset_ctrl); gen_rst.fpga_soft_reset(reset_ctrl); end endtask
gen_rst は、 zynq_ultra_ps_e_vip_v1_0_vl_rfs.sv の11703行目に下記のようなモジュールのインスタンスとして宣言されています。
zynq_ultra_ps_e_vip_v1_0_12_gen_reset gen_rst
zynq_ultra_ps_e_vip_v1_0_12_gen_reset は、zynq_ultra_ps_e_vip_v1_0_vl_rfs.sv の3952行目から定義があります。
下記のように、fpga_soft_reset API では、引数の4ビットを fabric_rst_n[3:0] に代入しています。
task fpga_soft_reset; input[3:0] reset_ctrl; begin fabric_rst_n[0] = reset_ctrl[0]; fabric_rst_n[1] = reset_ctrl[1]; fabric_rst_n[2] = reset_ctrl[2]; fabric_rst_n[3] = reset_ctrl[3]; end endtask
fabric_rst_n[3:0] は、fclk_reset[3:0]_n に assign され、出力信号になっています。
assign fclk_reset0_n = !fabric_rst_n[0]; assign fclk_reset1_n = !fabric_rst_n[1]; assign fclk_reset2_n = !fabric_rst_n[2]; assign fclk_reset3_n = !fabric_rst_n[3];
一方、por_srstb_reset API では、下記のように、por_rst_n と sys_rst_n に引数の por_reset_ctrl を代入しています。
task por_srstb_reset; input por_reset_ctrl; begin por_rst_n = por_reset_ctrl; sys_rst_n = por_reset_ctrl; end endtask
por_rst_n と sys_rst_n は、下記のように AND され、rst_out_n に assign されています。なんで、こんな変なことしているんでしょうかね。
assign rst_out_n = por_rst_n & sys_rst_n;
下記のように、rst_out_n は、net_rstn に、fclk_reset[3:0]_n はPL_RESET[3:0] に接続されています。
zynq_ultra_ps_e_vip_v1_0_12_gen_reset gen_rst( .por_rst_n_dummy(PSS_ALTO_CORE_PAD_PORB), .sys_rst_n_dummy(PSS_ALTO_CORE_PAD_SRSTB), .rst_out_n (net_rstn), // 途中略 .fclk_reset3_n (PL_RESETN3), .fclk_reset2_n (PL_RESETN2), .fclk_reset1_n (PL_RESETN1), .fclk_reset0_n (PL_RESETN0) );
net_rstn は、zynq_ultra_ps_e_vip_v1_0_vl_rfs.sv 内の
- ddrc
- ocmc
- regc
- ddr_interconnect
- ocm_interconnect
- reg_interconnect
のリセット信号に接続しています。
PL_RESETN[3:0] は、zynq_ultra_ps_e_vip_v1_0_12 の出力になっています。
おわりに
今回は、
- read_mem / write_mem API
- read_register API
- set_stop_on_error API
- por_srstv_reset / fpga_soft_reset API
について、見てみました。