diff --git a/.github/verible.waiver b/.github/verible.waiver index 7dd6587f..a55b431d 100644 --- a/.github/verible.waiver +++ b/.github/verible.waiver @@ -6,3 +6,6 @@ # - Thomas Benz waive --rule=package-filename --location="src/frontend/inst64/idma_inst64_opcode.sv" + +# IDMA_DV / IDMA_ND_DV are the exported verification API; renaming breaks downstream +waive --rule=interface-name-style --location="test/idma_intf.sv" diff --git a/idma.mk b/idma.mk index 019ecb02..79309beb 100644 --- a/idma.mk +++ b/idma.mk @@ -21,8 +21,7 @@ VLIB ?= vlib # iDMA root, resolved via bender so integrators can host iDMA anywhere IDMA_ROOT ?= $(shell $(BENDER) path idma) -# All generator/doc tooling runs through uv against the locked environment -# (pyproject.toml + uv.lock are the single source of truth). +# All tooling runs through uv against the locked environment UV_RUN := $(UV) run --locked --project $(IDMA_ROOT) PYTHON ?= $(UV_RUN) python PEAKRDL ?= $(UV_RUN) peakrdl @@ -62,8 +61,7 @@ IDMA_JOBS_JSON := jobs/jobs.json IDMA_BENDER_FILES := $(IDMA_ROOT)/Bender.yml \ $(IDMA_ROOT)/Bender.lock -# Helper functions -# Relative paths for VLOGAN +# Helper functions Relative paths for VLOGAN IDMA_VLOGAN_REL_PATHS := | grep -v "ROOT=" | sed '3 i ROOT="../../.."' # Ensure half-built targets are purged @@ -181,8 +179,7 @@ IDMA_FE_DIR := $(IDMA_ROOT)/src/frontend IDMA_FE_REGS := desc64 IDMA_FE_REGS += $(IDMA_FE_IDS) -# Config-bus CPUIF for the register frontend: PeakRDL regblock --cpuif + matching wrapper -# packing. apb4-flat (default, industry standard); also obi-flat / passthrough / axi4-lite-flat. +# Config-bus CPUIF: apb4-flat default; also obi-flat, axi4-lite-flat, passthrough IDMA_REG_CPUIF ?= apb4-flat @@ -212,8 +209,7 @@ $(IDMA_RTL_DIR)/idma_reg%d_reg_pkg.sv $(IDMA_RTL_DIR)/idma_reg%d_reg_top.sv $(ID -P Log2NumDims=$(call log2dimension,$(call dimension,$*)) $(IDMA_RTL_DIR)/idma_desc64_reg_pkg.sv $(IDMA_RTL_DIR)/idma_desc64_reg_top.sv $(IDMA_RTL_DIR)/idma_desc64_addrmap_pkg.sv: - # desc64 has static, hand-written APB reg wrappers (idma_desc64_reg_wrapper.sv); it is - # APB-native and not part of the CPUIF selector — keep its reg_top apb4-flat. + # desc64 is APB-native with hand-written wrappers, outside the CPUIF selector $(PEAKRDL) regblock $(IDMA_FE_DIR)/desc64/idma_desc64_reg.rdl -o $(IDMA_RTL_DIR) \ --default-reset arst_n --cpuif apb4-flat \ --module-name idma_desc64_reg_top \ @@ -388,9 +384,7 @@ endef $(IDMA_VSIM_DIR)/compile.tcl: $(IDMA_BENDER_FILES) $(IDMA_FULL_TB) $(IDMA_FULL_RTL) $(IDMA_INCLUDE_ALL) $(IDMA_WAVE_ALL) $(call idma_generate_vsim, $@, -t sim -t test -t idma_test -t synth -t rtl -t asic -t snitch_cluster,../../..) -# Standalone self-checking transpose-engine regression (DPI-C golden, no backend deps). -# Run with the Questa SEPP wrapper, e.g.: -# make idma_sim_tb_idma_otf_transpose VSIM="questa-2023.4 vsim" VLOG="questa-2023.4 vlog" VLIB="questa-2023.4 vlib" +# Standalone transpose-engine regression against the DPI-C golden IDMA_OTF_TP_RTL := $(abspath $(IDMA_ROOT)/src/backend/idma_otf_transpose.sv) IDMA_OTF_TP_TB := $(abspath $(IDMA_ROOT)/test/tb_idma_otf_transpose.sv) IDMA_OTF_TP_DPI := $(abspath $(IDMA_ROOT)/test/idma_transpose_dpi.c) @@ -403,13 +397,12 @@ idma_sim_tb_idma_otf_transpose: cd $(IDMA_OTF_TP_DIR); $(VLOG) -sv $(IDMA_OTF_TP_DPI) cd $(IDMA_OTF_TP_DIR); $(VLOG) -sv -svinputport=compat -timescale "1ns/1fs" $(IDMA_OTF_TP_RTL) $(IDMA_OTF_TP_TB) # the TB sweeps the geometry list internally; one run per StrbWidth x FullDuplex - cd $(IDMA_OTF_TP_DIR); $(VSIM) -c -t 1ps -gStrbWidth=8 -gFullDuplex=1 tb_idma_otf_transpose +BP -do "run -all; quit" - cd $(IDMA_OTF_TP_DIR); $(VSIM) -c -t 1ps -gStrbWidth=8 -gFullDuplex=0 tb_idma_otf_transpose +BP -do "run -all; quit" - cd $(IDMA_OTF_TP_DIR); $(VSIM) -c -t 1ps -gStrbWidth=64 -gFullDuplex=1 tb_idma_otf_transpose +BP -do "run -all; quit" - cd $(IDMA_OTF_TP_DIR); $(VSIM) -c -t 1ps -gStrbWidth=64 -gFullDuplex=0 tb_idma_otf_transpose +BP -do "run -all; quit" + cd $(IDMA_OTF_TP_DIR); $(VSIM) -c -t 1ps -gStrbWidth=8 -gFullDuplex=1 tb_idma_otf_transpose +BP=1 -do "run -all; quit" + cd $(IDMA_OTF_TP_DIR); $(VSIM) -c -t 1ps -gStrbWidth=8 -gFullDuplex=0 tb_idma_otf_transpose +BP=1 -do "run -all; quit" + cd $(IDMA_OTF_TP_DIR); $(VSIM) -c -t 1ps -gStrbWidth=64 -gFullDuplex=1 tb_idma_otf_transpose +BP=1 -do "run -all; quit" + cd $(IDMA_OTF_TP_DIR); $(VSIM) -c -t 1ps -gStrbWidth=64 -gFullDuplex=0 tb_idma_otf_transpose +BP=1 -do "run -all; quit" -# Multi-tile transpose via the ND midend -> rw_axi backend -> axi_sim_mem. -# Run with the Questa SEPP wrapper: make idma_sim_tb_idma_transpose_nd VSIM="questa-2023.4 vsim" +# Multi-tile transpose: ND midend to rw_axi backend to axi_sim_mem .PHONY: idma_sim_tb_idma_transpose_nd idma_sim_tb_idma_transpose_nd: $(IDMA_VSIM_DIR)/compile.tcl cd $(IDMA_VSIM_DIR); $(VSIM) -c -do "source compile.tcl; quit" @@ -417,11 +410,7 @@ idma_sim_tb_idma_transpose_nd: $(IDMA_VSIM_DIR)/compile.tcl cd $(IDMA_VSIM_DIR); $(VSIM) -c -t 1ps -voptargs=+acc -gDataWidth=32 tb_idma_transpose_nd -do "run -all; quit" cd $(IDMA_VSIM_DIR); $(VSIM) -c -t 1ps -voptargs=+acc -gDataWidth=64 tb_idma_transpose_nd -do "run -all; quit" -# Back-to-back regressions: the ND midend must reload each new transfer's base -# address (it does, for a protocol-compliant producer that drops nd_req_valid on -# accept). tb_idma_nd_midend_b2b checks the midend's burst-address sequence under -# backpressure; tb_idma_transpose_b2b checks two end-to-end transposes to distinct -# destinations. Run with the Questa SEPP wrapper. +# Back-to-back: the ND midend must reload each transfer base address .PHONY: idma_sim_tb_idma_nd_midend_b2b idma_sim_tb_idma_nd_midend_b2b: $(IDMA_VSIM_DIR)/compile.tcl cd $(IDMA_VSIM_DIR); $(VSIM) -c -do "source compile.tcl; quit" @@ -450,9 +439,7 @@ idma_sim_tb_idma_transpose_b2b: $(IDMA_VSIM_DIR)/compile.tcl cd $(IDMA_VSIM_DIR); $(VSIM) -c -t 1ps -voptargs=+acc -gDataWidth=32 tb_idma_transpose_b2b -do "run -all; quit" cd $(IDMA_VSIM_DIR); $(VSIM) -c -t 1ps -voptargs=+acc -gDataWidth=64 tb_idma_transpose_b2b -do "run -all; quit" -# Run a self-checking MX sim across data widths. Questa does not propagate -# $fatal to the exit code, so fail on any Error:/Fatal: in the run log. -# $(1) = testbench, $(2) = space-separated data widths. +# MX sim across data widths; Questa hides $fatal, so grep the transcript define idma_run_mx_sim cd $(IDMA_VSIM_DIR); set -e; for dw in $(2); do \ $(VSIM) -c -t 1ps -voptargs=+acc -gDataWidth=$$dw $(1) -do "run -all; quit" > $(1)_$$dw.log 2>&1 || true; \ @@ -624,13 +611,19 @@ IDMA_LINT_TOPS ?= $(addprefix idma_backend_synth_,$(IDMA_BACKEND_IDS)) \ idma_mp_midend_synth \ idma_rt_midend_synth -# lint-sv is -diff scoped; this checks all of src/ +# lint-sv is -diff scoped; this checks all of src/ and test/ VERIBLE ?= verible-verilog-lint +IDMA_LINT_SV_DIRS := $(IDMA_ROOT)/src $(IDMA_ROOT)/test + +# Vendored third-party sources, exempt here and in authors-cfg.yaml +IDMA_LINT_SV_VENDORED := test/future/TLToAXI4.v test/future/idma_tb_per2axi.sv + .PHONY: idma_lint_sv idma_lint_sv: $(VERIBLE) --waiver_files $(IDMA_ROOT)/.github/verible.waiver \ - $$(find $(IDMA_ROOT)/src -name '*.sv' -o -name '*.svh' | sort) + $$(find $(IDMA_LINT_SV_DIRS) \( -name '*.sv' -o -name '*.svh' \) \ + $(foreach f,$(IDMA_LINT_SV_VENDORED),! -path '*/$(f)') | sort) .PHONY: idma_lint_elab idma_lint_elab: diff --git a/test/frontend/idma_inst64_drv_if.sv b/test/frontend/idma_inst64_drv_if.sv index 3a645cf0..2f21c44f 100644 --- a/test/frontend/idma_inst64_drv_if.sv +++ b/test/frontend/idma_inst64_drv_if.sv @@ -63,17 +63,16 @@ interface idma_inst64_drv_if #( //-------------------------------------- // Response capture //-------------------------------------- - // acc_res_o comes out of a 2-deep cc_spill_register; with acc_res_ready_i tied high - // it pops on the posedge, so sample it in a clocked process instead of reading it - // combinationally from a task. + // acc_res_o pops on the posedge, so sample it in a clocked process acc_rsp_item_t rsp_queue [$]; always_ff @(posedge clk) begin : proc_capture_rsp // built in a variable first: verilator rejects an assignment pattern as an argument automatic acc_rsp_item_t rsp_item; if (rst_n && acc_res_valid && acc_res_ready) begin - rsp_item = '{id: acc_res.id, data: acc_res.data, error: acc_res.error}; - rsp_queue.push_back(rsp_item); + // typed pattern: verilator rejects a bare assignment pattern as a call argument + rsp_queue.push_back( + acc_rsp_item_t'{id: acc_res.id, data: acc_res.data, error: acc_res.error}); end end @@ -84,9 +83,7 @@ interface idma_inst64_drv_if #( //-------------------------------------- // Low-level accelerator request driver //-------------------------------------- - // Drive at ApplDelay, sample ready at AcqDelay of the SAME cycle, then release on the - // handshake edge. Sampling ready in the same delta as the drive reads the stale value - // and holds valid across two edges, which issues every instruction twice. + // Sampling ready in the drive delta issues every instruction twice task automatic acc_issue( input logic [31:0] data_op, input logic [63:0] data_arga, @@ -113,8 +110,7 @@ interface idma_inst64_drv_if #( acc_req_valid = 1'b0; endtask - /// Pop the response the DUT produced for the last issued request; fails on timeout, - /// on an id mismatch or on a flagged error. + // / Pop the response for the last request; fails on timeout, id mismatch or error task automatic acc_get_rsp(output acc_rsp_item_t item); int unsigned waited; waited = 0; @@ -203,8 +199,7 @@ interface idma_inst64_drv_if #( transfer_id = item.data[31:0]; endtask - /// Register-form status read; argb[1:0] = index, argb[4:2] = channel. - /// index 0 = completed_id, 1 = next_id, 2 = busy, 3 = fifo full + // / Status read; index 0 = completed_id, 1 = next_id, 2 = busy, 3 = fifo full task automatic dma_poll_status( input logic [1:0] status_idx, input logic [2:0] channel, @@ -217,8 +212,7 @@ interface idma_inst64_drv_if #( status_value = item.data; endtask - /// Wait for `transfer_id` to retire. The id generator resets to next=2/completed=1, so - /// the first transfer gets id 2 and the compare below is not vacuous. + // / Wait for retire; ids start at 2, so the compare is not vacuous task automatic dma_wait( input tf_id_t transfer_id, input logic [2:0] channel diff --git a/test/frontend/tb_idma_desc64_bench.sv b/test/frontend/tb_idma_desc64_bench.sv index 442bc4d3..4d7c54f3 100644 --- a/test/frontend/tb_idma_desc64_bench.sv +++ b/test/frontend/tb_idma_desc64_bench.sv @@ -52,10 +52,10 @@ module tb_idma_desc64_bench parameter int unsigned Seed = 1337 ) (); localparam time PERIOD = 10ns; - localparam time APPL_DELAY = PERIOD / 4; - localparam time ACQ_DELAY = PERIOD * 3 / 4; + localparam time ApplDelay = PERIOD / 4; + localparam time AcqDelay = PERIOD * 3 / 4; - localparam integer RESET_CYCLES = 10; + localparam integer ResetCycles = 10; localparam integer DataWidth = 64; localparam integer AddrWidth = 64; @@ -71,8 +71,10 @@ module tb_idma_desc64_bench typedef axi_test::axi_b_beat #(.IW(3), .UW(1)) b_beat_t; `APB_TYPEDEF_ALL(apb, /* addr */ addr_t, /* data */ logic [63:0], /* strobe */ logic [7:0]) - `AXI_TYPEDEF_ALL(axi, /* addr */ addr_t, /* id */ axi_id_t, /* data */ logic [63:0], /* strb */ logic [7:0], /* user */ logic [0:0]) - `AXI_TYPEDEF_ALL(mem_axi, /* addr */ addr_t, /* id */ mem_axi_id_t, /* data */ logic [63:0], /* strb */ logic [7:0], /* user */ logic [0:0]) + `AXI_TYPEDEF_ALL(axi, /* addr */ addr_t, /* id */ axi_id_t, /* data */ logic [63:0], + /* strb */ logic [7:0], /* user */ logic [0:0]) + `AXI_TYPEDEF_ALL(mem_axi, /* addr */ addr_t, /* id */ mem_axi_id_t, /* data */ logic [63:0], + /* strb */ logic [7:0], /* user */ logic [0:0]) // iDMA struct definitions typedef logic [TFLenWidth-1:0] tf_len_t; @@ -109,28 +111,44 @@ module tb_idma_desc64_bench addr_t next = 64'hffff_ffff_ffff_ffff; // an entire descriptor of 4 words must fit before the end of memory - constraint descriptor_fits_in_memory { (64'hffff_ffff_ffff_ffff - base) > 64'd32; } - constraint descriptor_is_in_descriptor_area { base > 64'h0000_ffff_ffff_ffff; } - constraint descriptor_is_aligned { (base & 64'hf) == 0; } - constraint no_empty_transfers { burst.length > '0; } - constraint src_fits_in_memory { 64'hffff_ffff_ffff_ffff - burst.src_addr > burst.length; } - constraint dst_fits_in_memory { 64'hffff_ffff_ffff_ffff - burst.dst_addr > burst.length; } - constraint src_is_not_in_descriptor_area { 64'h0000_ffff_ffff_ffff > (burst.src_addr + burst.length); } - constraint dst_is_not_in_descriptor_area { 64'h0000_ffff_ffff_ffff > (burst.dst_addr + burst.length); } - constraint src_aligned { (burst.src_addr & AlignmentMask) == 64'b0; } - constraint dst_aligned { (burst.dst_addr & AlignmentMask) == 64'b0; } - constraint src_burst_valid { burst.opt.src.burst inside { BURST_INCR }; } - constraint dst_burst_valid { burst.opt.dst.burst inside { BURST_INCR }; } - constraint reduce_len_equal { burst.opt.beo.src_reduce_len == burst.opt.beo.dst_reduce_len; } - constraint reduce_len_zero { burst.opt.beo.src_reduce_len == 1'b0; } - constraint beo_zero { burst.opt.beo.decouple_aw == '0 && burst.opt.beo.src_max_llen == '0 && burst.opt.beo.dst_max_llen == '0 && burst.opt.last == '0 && burst.opt.beo.decouple_rw == '0; } - constraint axi_params_zero_src { burst.opt.src.lock == '0 && burst.opt.src.prot == '0 && burst.opt.src.qos == '0 && burst.opt.src.region == '0; } - constraint axi_params_zero_dst { burst.opt.dst.lock == '0 && burst.opt.dst.prot == '0 && burst.opt.dst.qos == '0 && burst.opt.dst.region == '0; } - constraint axi_src_cache_zero { burst.opt.src.cache == '0; } - constraint axi_dst_cache_zero { burst.opt.dst.cache == '0; } - constraint compute_zero { burst.opt.compute == '0; } - constraint transfer_length { burst.length == TransferLength; } - constraint irq { do_irq == DoIRQ; } + constraint descriptor_fits_in_memory_c { (64'hffff_ffff_ffff_ffff - base) > 64'd32; } + constraint descriptor_is_in_descriptor_area_c { base > 64'h0000_ffff_ffff_ffff; } + constraint descriptor_is_aligned_c { (base & 64'hf) == 0; } + constraint no_empty_transfers_c { burst.length > '0; } + constraint src_fits_in_memory_c { 64'hffff_ffff_ffff_ffff - burst.src_addr > burst.length; } + constraint dst_fits_in_memory_c { 64'hffff_ffff_ffff_ffff - burst.dst_addr > burst.length; } + constraint src_is_not_in_descriptor_area_c { + 64'h0000_ffff_ffff_ffff > (burst.src_addr + burst.length); + } + constraint dst_is_not_in_descriptor_area_c { + 64'h0000_ffff_ffff_ffff > (burst.dst_addr + burst.length); + } + constraint src_aligned_c { (burst.src_addr & AlignmentMask) == 64'b0; } + constraint dst_aligned_c { (burst.dst_addr & AlignmentMask) == 64'b0; } + constraint src_burst_valid_c { burst.opt.src.burst inside { BURST_INCR }; } + constraint dst_burst_valid_c { burst.opt.dst.burst inside { BURST_INCR }; } + constraint reduce_len_equal_c { + burst.opt.beo.src_reduce_len == burst.opt.beo.dst_reduce_len; + } + constraint reduce_len_zero_c { burst.opt.beo.src_reduce_len == 1'b0; } + constraint beo_zero_c { + burst.opt.beo.decouple_aw == '0 && burst.opt.beo.src_max_llen == '0 && + burst.opt.beo.dst_max_llen == '0 && burst.opt.last == '0 && + burst.opt.beo.decouple_rw == '0; + } + constraint axi_params_zero_src_c { + burst.opt.src.lock == '0 && burst.opt.src.prot == '0 && + burst.opt.src.qos == '0 && burst.opt.src.region == '0; + } + constraint axi_params_zero_dst_c { + burst.opt.dst.lock == '0 && burst.opt.dst.prot == '0 && + burst.opt.dst.qos == '0 && burst.opt.dst.region == '0; + } + constraint axi_src_cache_zero_c { burst.opt.src.cache == '0; } + constraint axi_dst_cache_zero_c { burst.opt.dst.cache == '0; } + constraint compute_zero_c { burst.opt.compute == '0; } + constraint transfer_length_c { burst.length == TransferLength; } + constraint irq_c { do_irq == DoIRQ; } endclass typedef struct { @@ -152,7 +170,7 @@ module tb_idma_desc64_bench clk_rst_gen #( .ClkPeriod(PERIOD), - .RstClkCycles(RESET_CYCLES) + .RstClkCycles(ResetCycles) ) i_clock_reset_generator ( .clk_o (clk) , .rst_no(rst_n) @@ -167,8 +185,8 @@ module tb_idma_desc64_bench apb_driver #( .ADDR_WIDTH(64), .DATA_WIDTH(64), - .TA(APPL_DELAY), - .TT(ACQ_DELAY) + .TA(ApplDelay), + .TT(AcqDelay) ) i_apb_driver = new (i_apb_iface_bus); axi_resp_t dma_fe_master_response; @@ -203,8 +221,8 @@ module tb_idma_desc64_bench .DW(64), .IW(3), .UW(1), - .TA(APPL_DELAY), - .TT(ACQ_DELAY) + .TA(ApplDelay), + .TT(AcqDelay) ) i_axi_iface_driver = new (i_axi_iface_bus); apb_resp_t dma_slave_response; @@ -303,7 +321,7 @@ module tb_idma_desc64_bench // DMA Tracer //-------------------------------------- // only activate tracer if requested - if (DmaTracing) begin + if (DmaTracing) begin : gen_dma_tracer // fetch the name of the trace file from CMD line string trace_file; initial begin @@ -376,8 +394,8 @@ module tb_idma_desc64_bench .axi_rsp_t (mem_axi_resp_t), .WarnUninitialized ( 1'b0 ), .ClearErrOnAccess ( 1'b1 ), - .ApplDelay ( APPL_DELAY ), - .AcqDelay ( ACQ_DELAY ) + .ApplDelay ( ApplDelay ), + .AcqDelay ( AcqDelay ) ) i_axi_sim_mem ( .clk_i ( clk ), .rst_ni ( rst_n ), @@ -567,10 +585,7 @@ module tb_idma_desc64_bench end generated_stimuli.push_back(current_stimuli_group); end - // make the last stimulus generate an irq to simplify the IRQ - // acquisition - // NOTE: with few requests this might impact statitics of the no-IRQ - // case + // last stimulus raises an irq to simplify acquisition generated_stimuli[$][$].do_irq = 1'b1; golden_queue[$].did_irq = 1'b1; endfunction : generate_stimuli @@ -603,13 +618,13 @@ module tb_idma_desc64_bench end endfunction : load_descriptors_into_memory - task apply_stimuli(); + task automatic apply_stimuli(); fork regbus_slave_interaction(); join endtask - task collect_responses(); + task automatic collect_responses(); fork axi_master_acquire_ars(); axi_master_acquire_rs(); @@ -621,7 +636,7 @@ module tb_idma_desc64_bench endtask // regbus slave interaction (we're acting as master) - task regbus_slave_interaction(); + task automatic regbus_slave_interaction(); automatic stimulus_t current_stimulus_group[$]; i_apb_driver.reset_master(); @(posedge rst_n); @@ -682,7 +697,7 @@ module tb_idma_desc64_bench return result; endfunction - task axi_master_acquire_ars(); + task automatic axi_master_acquire_ars(); @(posedge rst_n); forever begin automatic ax_beat_t ar_beat; @@ -697,7 +712,7 @@ module tb_idma_desc64_bench end endtask : axi_master_acquire_ars - task axi_master_acquire_rs(); + task automatic axi_master_acquire_rs(); @(posedge rst_n); forever begin automatic r_beat_t r_beat; @@ -706,8 +721,7 @@ module tb_idma_desc64_bench current_result = ar_seen_result.pop_front(); i_axi_iface_driver.mon_r(r_beat); if ($isunknown(r_beat.r_data)) begin - // drop current result - // as it is a prefetched one + // drop current result as it is a prefetched one end else begin inflight_results_after_reads.push_back(current_result); end @@ -721,7 +735,7 @@ module tb_idma_desc64_bench end endtask : axi_master_acquire_rs - task axi_master_acquire_aw(); + task automatic axi_master_acquire_aw(); // set to one to skip first submission of what would be an invalid result automatic result_t current_result; @(posedge rst_n); @@ -738,7 +752,7 @@ module tb_idma_desc64_bench end endtask - task axi_master_acquire_w(); + task automatic axi_master_acquire_w(); automatic result_t current_result; @(posedge rst_n); forever begin @@ -751,15 +765,14 @@ module tb_idma_desc64_bench end endtask : axi_master_acquire_w - task axi_master_acquire_irqs(); + task automatic axi_master_acquire_irqs(); automatic result_t current_result; @(posedge rst_n); forever begin automatic b_beat_t b_beat; automatic result_t current_result; - // HACK: I'm taking advantage of the knowledge that the irq and - // B happen in the same cycle + // relies on the irq and B landing in the same cycle i_axi_iface_driver.mon_b(b_beat); wait(w_seen_result.size() > 0); current_result = w_seen_result.pop_front(); @@ -768,14 +781,14 @@ module tb_idma_desc64_bench end endtask : axi_master_acquire_irqs - task acquire_bursts(); + task automatic acquire_bursts(); automatic result_t current_result; automatic idma_req_t current_burst; @(posedge rst_n); forever begin forever begin @(posedge clk); - #(ACQ_DELAY); + #(AcqDelay); if (dma_be_req_valid && dma_be_req_ready) break; end current_burst = dma_be_req; diff --git a/test/frontend/tb_idma_desc64_top.sv b/test/frontend/tb_idma_desc64_top.sv index ebc53f2c..d47f1127 100644 --- a/test/frontend/tb_idma_desc64_top.sv +++ b/test/frontend/tb_idma_desc64_top.sv @@ -40,10 +40,10 @@ module tb_idma_desc64_top parameter int unsigned Seed = 1337 ) (); localparam time PERIOD = 10ns; - localparam time APPL_DELAY = PERIOD / 4; - localparam time ACQ_DELAY = PERIOD * 3 / 4; + localparam time ApplDelay = PERIOD / 4; + localparam time AcqDelay = PERIOD * 3 / 4; - localparam integer RESET_CYCLES = 10; + localparam integer ResetCycles = 10; typedef logic [63:0] addr_t; typedef logic [ 2:0] axi_id_t; @@ -53,7 +53,8 @@ module tb_idma_desc64_top typedef axi_test::axi_b_beat #(.IW(3), .UW(1)) b_beat_t; `APB_TYPEDEF_ALL(apb, /* addr */ addr_t, /* data */ logic [63:0], /* strobe */ logic [7:0]) - `AXI_TYPEDEF_ALL(axi, /* addr */ addr_t, /* id */ axi_id_t, /* data */ logic [63:0], /* strb */ logic [7:0], /* user */ logic [0:0]) + `AXI_TYPEDEF_ALL(axi, /* addr */ addr_t, /* id */ axi_id_t, /* data */ logic [63:0], + /* strb */ logic [7:0], /* user */ logic [0:0]) // iDMA struct definitions localparam int unsigned TFLenWidth = 32; @@ -75,27 +76,47 @@ module tb_idma_desc64_top addr_t next = 64'hffff_ffff_ffff_ffff; // an entire descriptor of 4 words must fit before the end of memory - constraint descriptor_fits_in_memory { (64'hffff_ffff_ffff_ffff - base) > 64'd32; } - constraint descriptor_is_in_descriptor_area { base > 64'h0000_ffff_ffff_ffff; } - constraint descriptor_is_aligned { (base & 64'hf) == 0; } - constraint no_empty_transfers { burst.length > '0; } - constraint src_fits_in_memory { 64'hffff_ffff_ffff_ffff - burst.src_addr > burst.length; } - constraint dst_fits_in_memory { 64'hffff_ffff_ffff_ffff - burst.dst_addr > burst.length; } - constraint src_burst_valid { burst.opt.src.burst inside { BURST_INCR, BURST_WRAP, BURST_FIXED }; } - constraint dst_burst_valid { burst.opt.dst.burst inside { BURST_INCR, BURST_WRAP, BURST_FIXED }; } - constraint src_is_not_in_descriptor_area { 64'h0000_ffff_ffff_ffff > (burst.src_addr + burst.length); } - constraint dst_is_not_in_descriptor_area { 64'h0000_ffff_ffff_ffff > (burst.dst_addr + burst.length); } - constraint src_aligned { (burst.src_addr & AlignmentMask) == 64'b0; } - constraint dst_aligned { (burst.dst_addr & AlignmentMask) == 64'b0; } - constraint reduce_len_equal { burst.opt.beo.src_reduce_len == burst.opt.beo.dst_reduce_len; } - constraint reduce_len_zero { burst.opt.beo.src_reduce_len == 1'b0; } - constraint beo_zero { burst.opt.beo.decouple_aw == '0 && burst.opt.beo.src_max_llen == '0 && burst.opt.beo.dst_max_llen == '0 && burst.opt.last == '0 && burst.opt.beo.decouple_rw == '0; } - constraint axi_params_zero_src { burst.opt.src.lock == '0 && burst.opt.src.prot == '0 && burst.opt.src.qos == '0 && burst.opt.src.region == '0; } - constraint axi_params_zero_dst { burst.opt.dst.lock == '0 && burst.opt.dst.prot == '0 && burst.opt.dst.qos == '0 && burst.opt.dst.region == '0; } - constraint axi_src_cache_zero { burst.opt.src.cache == '0; } - constraint axi_dst_cache_zero { burst.opt.dst.cache == '0; } - constraint compute_zero { burst.opt.compute == '0; } - constraint transfer_length { burst.length == TransferLength; } + constraint descriptor_fits_in_memory_c { (64'hffff_ffff_ffff_ffff - base) > 64'd32; } + constraint descriptor_is_in_descriptor_area_c { base > 64'h0000_ffff_ffff_ffff; } + constraint descriptor_is_aligned_c { (base & 64'hf) == 0; } + constraint no_empty_transfers_c { burst.length > '0; } + constraint src_fits_in_memory_c { 64'hffff_ffff_ffff_ffff - burst.src_addr > burst.length; } + constraint dst_fits_in_memory_c { 64'hffff_ffff_ffff_ffff - burst.dst_addr > burst.length; } + constraint src_burst_valid_c { + burst.opt.src.burst inside { BURST_INCR, BURST_WRAP, BURST_FIXED }; + } + constraint dst_burst_valid_c { + burst.opt.dst.burst inside { BURST_INCR, BURST_WRAP, BURST_FIXED }; + } + constraint src_is_not_in_descriptor_area_c { + 64'h0000_ffff_ffff_ffff > (burst.src_addr + burst.length); + } + constraint dst_is_not_in_descriptor_area_c { + 64'h0000_ffff_ffff_ffff > (burst.dst_addr + burst.length); + } + constraint src_aligned_c { (burst.src_addr & AlignmentMask) == 64'b0; } + constraint dst_aligned_c { (burst.dst_addr & AlignmentMask) == 64'b0; } + constraint reduce_len_equal_c { + burst.opt.beo.src_reduce_len == burst.opt.beo.dst_reduce_len; + } + constraint reduce_len_zero_c { burst.opt.beo.src_reduce_len == 1'b0; } + constraint beo_zero_c { + burst.opt.beo.decouple_aw == '0 && burst.opt.beo.src_max_llen == '0 && + burst.opt.beo.dst_max_llen == '0 && burst.opt.last == '0 && + burst.opt.beo.decouple_rw == '0; + } + constraint axi_params_zero_src_c { + burst.opt.src.lock == '0 && burst.opt.src.prot == '0 && + burst.opt.src.qos == '0 && burst.opt.src.region == '0; + } + constraint axi_params_zero_dst_c { + burst.opt.dst.lock == '0 && burst.opt.dst.prot == '0 && + burst.opt.dst.qos == '0 && burst.opt.dst.region == '0; + } + constraint axi_src_cache_zero_c { burst.opt.src.cache == '0; } + constraint axi_dst_cache_zero_c { burst.opt.dst.cache == '0; } + constraint compute_zero_c { burst.opt.compute == '0; } + constraint transfer_length_c { burst.length == TransferLength; } endclass typedef struct { @@ -117,7 +138,7 @@ module tb_idma_desc64_top clk_rst_gen #( .ClkPeriod(PERIOD), - .RstClkCycles(RESET_CYCLES) + .RstClkCycles(ResetCycles) ) i_clock_reset_generator ( .clk_o (clk) , .rst_no(rst_n) @@ -132,8 +153,8 @@ module tb_idma_desc64_top apb_driver #( .ADDR_WIDTH(64), .DATA_WIDTH(64), - .TA(APPL_DELAY), - .TT(ACQ_DELAY) + .TA(ApplDelay), + .TT(AcqDelay) ) i_apb_driver = new (i_apb_iface_bus); axi_resp_t dma_master_response; @@ -151,8 +172,8 @@ module tb_idma_desc64_top .DW(64), .IW(3), .UW(1), - .TA(APPL_DELAY), - .TT(ACQ_DELAY) + .TA(ApplDelay), + .TT(AcqDelay) ) i_axi_iface_driver = new (i_axi_iface_bus); apb_resp_t dma_slave_response; @@ -212,8 +233,8 @@ module tb_idma_desc64_top .axi_rsp_t (axi_resp_t), .WarnUninitialized (1'b0 ), .ClearErrOnAccess (1'b1 ), - .ApplDelay (APPL_DELAY), - .AcqDelay (ACQ_DELAY ) + .ApplDelay (ApplDelay), + .AcqDelay (AcqDelay ) ) i_axi_sim_mem ( .clk_i ( clk ), .rst_ni ( rst_n ), @@ -272,10 +293,12 @@ module tb_idma_desc64_top automatic int number_of_descriptors_in_test; if (ChainedDescriptors < 0) begin - void'(std::randomize(number_of_descriptors_in_test) with { + if (!std::randomize(number_of_descriptors_in_test) with { number_of_descriptors_in_test >= MinChainedDescriptors; number_of_descriptors_in_test <= MaxChainedDescriptors; - }); + }) begin + $error("Couldn't randomize the descriptor count"); + end end else begin number_of_descriptors_in_test = ChainedDescriptors; end @@ -353,8 +376,7 @@ module tb_idma_desc64_top end generated_stimuli.push_back(current_stimuli_group); end - // make the last stimulus generate an irq to simplify the IRQ - // acquisition + // make the last stimulus generate an irq to simplify the IRQ acquisition generated_stimuli[$][$].do_irq = 1'b1; golden_queue[$].did_irq = 1'b1; endfunction : generate_stimuli @@ -387,7 +409,7 @@ module tb_idma_desc64_top end endfunction : load_descriptors_into_memory - task apply_stimuli(); + task automatic apply_stimuli(); fork apb_slave_interaction(); backend_tx_done_notifier(); @@ -395,7 +417,7 @@ module tb_idma_desc64_top join endtask - task collect_responses(); + task automatic collect_responses(); fork axi_master_acquire_ars(); axi_master_acquire_rs(); @@ -405,7 +427,7 @@ module tb_idma_desc64_top endtask // apb slave interaction (we're acting as master) - task apb_slave_interaction(); + task automatic apb_slave_interaction(); automatic stimulus_t current_stimulus_group[$]; i_apb_driver.reset_master(); @(posedge rst_n); @@ -467,7 +489,7 @@ module tb_idma_desc64_top return result; endfunction - task axi_master_acquire_ars(); + task automatic axi_master_acquire_ars(); @(posedge rst_n); forever begin automatic ax_beat_t ar_beat; @@ -482,7 +504,7 @@ module tb_idma_desc64_top end endtask : axi_master_acquire_ars - task axi_master_acquire_rs(); + task automatic axi_master_acquire_rs(); @(posedge rst_n); forever begin automatic r_beat_t r_beat; @@ -491,8 +513,7 @@ module tb_idma_desc64_top current_result = ar_seen_result.pop_front(); i_axi_iface_driver.mon_r(r_beat); if ($isunknown(r_beat.r_data)) begin - // drop current result - // as it is a prefetched one + // drop current result as it is a prefetched one end else begin inflight_results_after_reads.push_back(current_result); end @@ -506,7 +527,7 @@ module tb_idma_desc64_top end endtask : axi_master_acquire_rs - task axi_master_acquire_aw_w_and_irqs(); + task automatic axi_master_acquire_aw_w_and_irqs(); fork axi_master_acquire_aw(); axi_master_acquire_w(); @@ -514,7 +535,7 @@ module tb_idma_desc64_top join endtask : axi_master_acquire_aw_w_and_irqs - task axi_master_acquire_aw(); + task automatic axi_master_acquire_aw(); // set to one to skip first submission of what would be an invalid result automatic result_t current_result; @(posedge rst_n); @@ -530,7 +551,7 @@ module tb_idma_desc64_top aw_seen_result.push_back(current_result); end endtask - task axi_master_acquire_w(); + task automatic axi_master_acquire_w(); automatic result_t current_result; @(posedge rst_n); forever begin @@ -542,15 +563,14 @@ module tb_idma_desc64_top w_seen_result.push_back(current_result); end endtask : axi_master_acquire_w - task axi_master_acquire_irqs(); + task automatic axi_master_acquire_irqs(); automatic result_t current_result; @(posedge rst_n); forever begin automatic b_beat_t b_beat; automatic result_t current_result; - // HACK: I'm taking advantage of the knowledge that the irq and - // B happen in the same cycle + // relies on the irq and B landing in the same cycle i_axi_iface_driver.mon_b(b_beat); wait(w_seen_result.size() > 0); current_result = w_seen_result.pop_front(); @@ -559,7 +579,7 @@ module tb_idma_desc64_top end endtask : axi_master_acquire_irqs - task backend_tx_done_notifier(); + task automatic backend_tx_done_notifier(); automatic int unsigned rand_success, cycles; @(posedge rst_n); forever begin @@ -574,25 +594,25 @@ module tb_idma_desc64_top repeat (cycles) @(posedge clk); /* END EXPAND RAND_WAIT FROM COMMON_VERIF_PKG */ - #(APPL_DELAY); + #(ApplDelay); dma_be_rsp_valid = 1'b1; wait (dma_be_rsp_ready); @(posedge clk); - #(APPL_DELAY); + #(ApplDelay); dma_be_rsp_valid = 1'b0; backend_busy = 1'b0; end endtask - task acquire_bursts(); + task automatic acquire_bursts(); automatic result_t current_result; automatic idma_req_t current_burst; @(posedge rst_n); forever begin forever begin @(posedge clk); - #(ACQ_DELAY); + #(AcqDelay); if (dma_be_req_valid && dma_be_req_ready) break; end current_burst = dma_be_req; @@ -603,23 +623,23 @@ module tb_idma_desc64_top end endtask - task backend_acceptor(); + task automatic backend_acceptor(); @(posedge rst_n); forever begin wait (!backend_busy); @(posedge clk); - #(APPL_DELAY) + #(ApplDelay) dma_be_req_ready = 1'b1; - #(ACQ_DELAY - APPL_DELAY); + #(AcqDelay - ApplDelay); forever begin if (dma_be_req_valid) begin break; end @(posedge clk); - #(ACQ_DELAY); + #(AcqDelay); end @(posedge clk); - #(APPL_DELAY) + #(ApplDelay) dma_be_req_ready = 1'b0; backend_busy = 1'b1; end @@ -641,7 +661,7 @@ module tb_idma_desc64_top static int irq_errors = 0; generate_stimuli(); - load_descriptors_into_memory(); + load_descriptors_into_memory(); fork apply_stimuli(); diff --git a/test/frontend/tb_idma_inst64_axi_copy.sv b/test/frontend/tb_idma_inst64_axi_copy.sv index a20722ad..bdbe87fd 100644 --- a/test/frontend/tb_idma_inst64_axi_copy.sv +++ b/test/frontend/tb_idma_inst64_axi_copy.sv @@ -17,16 +17,14 @@ module tb_idma_inst64_axi_copy; localparam int unsigned TimeoutCycles = 32'd200000; localparam int unsigned CopySize = 32'd4096; localparam int unsigned BytesPerBeat = AxiDataWidth / 32'd8; - // Keep the beat rounding: MaskInvalidData is 0 in the inst64 backend, so the full - // beat-rounded source range must be initialized once CopySize stops being a multiple. + // MaskInvalidData is 0 here, so the full beat-rounded range is read localparam int unsigned CopyPadBytes = ((CopySize + BytesPerBeat - 1) / BytesPerBeat) * BytesPerBeat; localparam int unsigned GuardBytes = 32'd64; localparam logic [7:0] Sentinel = 8'h5A; localparam logic [7:0] PatternStart = 8'hA0; - // One burst per direction: the copy is 64 B aligned on both ends and a multiple of a - // full beat, so every data beat carries a full strobe. + // One burst per direction: the copy is 64 B aligned and a whole beat localparam int unsigned ExpDataBeats = CopyPadBytes / BytesPerBeat; localparam axi_pkg::len_t ExpAxLen = axi_pkg::len_t'(ExpDataBeats - 1); localparam axi_pkg::size_t ExpAxSize = axi_pkg::size_t'($clog2(BytesPerBeat)); @@ -42,11 +40,7 @@ module tb_idma_inst64_axi_copy; //-------------------------------------- // DUT event cross-check //-------------------------------------- - // Every field recodes the DUT's own top-level pins, so the TB rebuilds the expected - // value from those pins and compares per cycle; the hand-counted AR/AW sniff stays - // the independent reference. Not covered: obi_wr_req/obi_rd_req (no OBI traffic - // here) and the stall fields, which never assert since the sim memories neither - // backpressure nor bubble. + // Each field recodes a DUT pin, so the TB rebuilds it from those pins typedef enum int unsigned { EvAwValid, EvAwReady, EvAwDone, EvAwStall, EvAwLen, EvAwSize, EvArValid, EvArReady, EvArDone, EvArStall, EvArLen, EvArSize, @@ -68,8 +62,7 @@ module tb_idma_inst64_axi_copy; logic [NumEvFields-1:0] ev_field_ok; - // len/size are compared in their full `handshake ? pin : '0` form every cycle, so a - // stale value leaking between bursts fails instead of hiding in the idle half. + // len/size compared every cycle, so a stale value fails immediately always_comb begin : proc_ev_field_ok automatic logic aw_hs = bus_req.aw_valid && bus_res.aw_ready; automatic logic ar_hs = bus_req.ar_valid && bus_res.ar_ready; @@ -110,8 +103,7 @@ module tb_idma_inst64_axi_copy; ev_field_ok[EvBusy ] = ev.dma_busy === harness.busy[0]; end - // One assertion per field so a failure names it. Both operands are combinational from - // the same pins and are sampled in the same preponed region, so the offset is zero. + // One assertion per field so a failure names it for (genvar f = 0; f < NumEvFields; f++) begin : gen_ev_field_check localparam ev_field_e Field = ev_field_e'(f); a_ev_field : assert property ( @@ -119,8 +111,7 @@ module tb_idma_inst64_axi_copy; ) else $fatal(1, "events.%s disagrees with the AXI pins", Field.name()); end - // events_o has no reset, so an X on both sides would make `===` match vacuously. - // This is a separate failure, never a reason to skip the comparison. + // events_o has no reset, so an X on both sides would match vacuously a_ev_pins_known : assert property ( @(posedge harness.clk) disable iff (!harness.rst_n) !$isunknown({bus_req.aw_valid, bus_req.ar_valid, bus_req.w_valid, bus_req.r_ready, @@ -139,22 +130,20 @@ module tb_idma_inst64_axi_copy; axi_pkg::len_t ev_ar_len_seen, ev_aw_len_seen; axi_pkg::size_t ev_ar_size_seen, ev_aw_size_seen; - // Count AXI address handshakes; a green OBI-never-asserted check is only meaningful - // if the transfer actually went somewhere. The DUT-side event totals accumulate in the - // same process so both views are read in the same region of the same tick. + // Count AXI handshakes; the OBI-never-asserted check needs real traffic always_ff @(posedge harness.clk) begin : proc_count_axi if (harness.rst_n) begin if (harness.axi_req[0].ar_valid && harness.axi_res[0].ar_ready) axi_ar_beats++; if (harness.axi_req[0].aw_valid && harness.axi_res[0].aw_ready) axi_aw_beats++; if (ev.ar_done) begin ev_ar_beats++; - ev_ar_len_seen = ev.ar_len; - ev_ar_size_seen = ev.ar_size; + ev_ar_len_seen <= ev.ar_len; + ev_ar_size_seen <= ev.ar_size; end if (ev.aw_done) begin ev_aw_beats++; - ev_aw_len_seen = ev.aw_len; - ev_aw_size_seen = ev.aw_size; + ev_aw_len_seen <= ev.aw_len; + ev_aw_size_seen <= ev.aw_size; end if (ev.r_done) ev_r_beats++; if (ev.r_bw) ev_r_bw_beats++; @@ -165,8 +154,7 @@ module tb_idma_inst64_axi_copy; end end - // Both endpoints sit outside the harness TCDM window, so the decoder must fall back to - // ToSoC/AXI. A rising OBI request means the protocol decode is wrong. + // Both endpoints sit outside the TCDM window, so the decoder picks AXI a_no_obi_traffic : assert property ( @(posedge harness.clk) disable iff (!harness.rst_n) !harness.obi_req[0].req ) else $fatal(1, "OBI leg requested during an AXI-to-AXI transfer: bad protocol decode"); @@ -175,8 +163,7 @@ module tb_idma_inst64_axi_copy; for (int i = 0; i < CopyPadBytes; i++) begin harness.mem_write_byte(SrcAddr + i, (i < CopySize) ? (PatternStart + i) : 8'h00); end - // Sentinel the destination and its guard bands so an unwritten byte or an - // overrun fails the compare rather than accidentally matching. + // Sentinel the destination and guard bands so an overrun fails the compare for (int unsigned i = 0; i < CopySize + 2*GuardBytes; i++) begin harness.mem_write_byte(DstAddr - GuardBytes + i, Sentinel); end @@ -235,8 +222,7 @@ module tb_idma_inst64_axi_copy; harness.drv_if.dma_start_copy(CopySize, 2'b00, 3'd0, tid); issued_req_id = harness.drv_if.last_req_id; - // The driver already fails hard on a bad response; re-check here so the TB states - // the contract it relies on. + // Re-checked here so the TB states the contract it relies on if (harness.drv_if.last_rsp_error !== 1'b0) begin $fatal(1, "DMCPY response flagged an error"); end @@ -256,8 +242,7 @@ module tb_idma_inst64_axi_copy; end if (CopySize == 0) $fatal(1, "zero-byte golden compare"); - // Exactly one transfer must have been launched. A driver that holds acc_req_valid - // one cycle too long issues the DMCPY twice; the memory image would look identical. + // Exactly one launch; a stuck acc_req_valid would issue two harness.drv_if.dma_poll_status(2'b01, 3'd0, next_id_after); if (next_id_after !== next_id_before + 1) begin $fatal(1, "next_id moved %0d -> %0d, expected exactly one transfer", @@ -273,9 +258,7 @@ module tb_idma_inst64_axi_copy; harness.drv_if.rsp_pending()); end - // Event totals: first against the independent sniff, then against the transfer - // geometry. The geometry anchors are what make the per-cycle equality above - // non-vacuous; without them a run that moved nothing would pass on 0 == 0. + // Event totals against the independent sniff, then the transfer geometry if (ev_ar_beats != axi_ar_beats || ev_aw_beats != axi_aw_beats) begin $fatal(1, "events ar/aw done (%0d/%0d) disagree with the bus sniff (%0d/%0d)", ev_ar_beats, ev_aw_beats, axi_ar_beats, axi_aw_beats); diff --git a/test/frontend/tb_idma_reg_frontend.sv b/test/frontend/tb_idma_reg_frontend.sv index 0da1ff32..505be703 100644 --- a/test/frontend/tb_idma_reg_frontend.sv +++ b/test/frontend/tb_idma_reg_frontend.sv @@ -36,32 +36,27 @@ module tb_idma_reg_frontend import idma_pkg::*; import apb_test::apb_driver; #( localparam int unsigned DataWidth = AddrWidth; localparam int unsigned NumDim = (RegVariant == 32'd3) ? 32'd3 : 32'd2; localparam int unsigned RepWidth = AddrWidth; - // apb_driver framing: the blocking driver.read() spans SETUP + first-ACCESS-check + - // trailing edge before returning, so a same-cycle (non-blocking) read takes this many - // config clocks end-to-end; each extra ACCESS wait state adds one more clock. + // apb_driver framing: a non-blocking read spans this many config clocks localparam int unsigned DrvFraming = 32'd2; - // bounded-latency bound for a non-blocking next_id read: measured ACCESS-phase latency - // (raw driver.read span minus DrvFraming) must be 0 config-clock cycles — the read must - // complete in its first ACCESS check even while req_ready_i is low. + // the non-blocking read must complete in its first ACCESS check localparam int unsigned MaxReadLatency = 32'd0; - // watchdog bound: any next_id APB read that does not complete within this many - // config-clock cycles is a hang (the non-blocking read completes immediately). + // watchdog: a next_id read outstanding this long is a hang localparam int unsigned DeadlockCycles = 32'd2000; // register map (idma_reg32_3d_addrmap_pkg): base + per-stream stride 0x4 - localparam logic [31:0] REG_CONF = 32'h0000_0000; - localparam logic [31:0] REG_STATUS0 = 32'h0000_0004; - localparam logic [31:0] REG_NEXT_ID0 = 32'h0000_0044; - localparam logic [31:0] REG_DONE_ID0 = 32'h0000_0084; - localparam logic [31:0] REG_DST_ADDR = 32'h0000_00D0; - localparam logic [31:0] REG_SRC_ADDR = 32'h0000_00D4; - localparam logic [31:0] REG_LENGTH = 32'h0000_00D8; + localparam logic [31:0] RegConf = 32'h0000_0000; + localparam logic [31:0] RegStatus0 = 32'h0000_0004; + localparam logic [31:0] RegNextId0 = 32'h0000_0044; + localparam logic [31:0] RegDoneId0 = 32'h0000_0084; + localparam logic [31:0] RegDstAddr = 32'h0000_00D0; + localparam logic [31:0] RegSrcAddr = 32'h0000_00D4; + localparam logic [31:0] RegLength = 32'h0000_00D8; function automatic logic [31:0] reg_next_id(input int unsigned s); - return REG_NEXT_ID0 + 32'(s) * 32'h4; + return RegNextId0 + 32'(s) * 32'h4; endfunction function automatic logic [31:0] reg_done_id(input int unsigned s); - return REG_DONE_ID0 + 32'(s) * 32'h4; + return RegDoneId0 + 32'(s) * 32'h4; endfunction // -------------------------------------------------------------------------- @@ -125,12 +120,9 @@ module tb_idma_reg_frontend import idma_pkg::*; import apb_test::apb_driver; #( logic [NumStreams-1:0] midend_busy; // -------------------------------------------------------------------------- - // APB DV interfaces + drivers: one per config port. Each interface is bridged - // to the DUT's packed dma_ctrl_req_i[i]/dma_ctrl_rsp_o[i] via the apb assign - // macros; one apb_driver per port lets Test 5 drive two ports concurrently. + // One APB DV interface and driver per config port, bridged to the packed DUT ports // -------------------------------------------------------------------------- - // virtual-interface handles (interface arrays can only be indexed by a constant, - // so each element is captured into this array from the generate loop below) + // interface arrays index only by constant, so each element is captured here typedef virtual APB_DV #(.ADDR_WIDTH(CfgAddrWidth), .DATA_WIDTH(CfgDataWidth)) apb_dv_t; apb_dv_t apb_vif[NumRegs]; apb_driver_t drv[NumRegs]; @@ -149,8 +141,7 @@ module tb_idma_reg_frontend import idma_pkg::*; import apb_test::apb_driver; #( end // -------------------------------------------------------------------------- - // Transfer-id generator (owns the next/completed counters). Reset next=2. - // issue on an accepted launch, retire on a modeled backend completion. + // Transfer-id generator: reset next=2, issue on launch, retire on completion // -------------------------------------------------------------------------- logic issue; logic retire; @@ -165,8 +156,7 @@ module tb_idma_reg_frontend import idma_pkg::*; import apb_test::apb_driver; #( .next_o ( next_id ), .completed_o ( done_id[0] ) ); - // multi-stream: id gen models stream 0; other streams share the same completed - // counter here (the DUT only compares per-stream done_id on read-back). + // multi-stream: the id gen models stream 0 and shares the completed counter for (genvar s = 1; s < NumStreams; s++) begin : gen_done_other assign done_id[s] = done_id[0]; end @@ -257,12 +247,7 @@ module tb_idma_reg_frontend import idma_pkg::*; import apb_test::apb_driver; #( end // -------------------------------------------------------------------------- - // Backend stub. `req_ready` is directly controllable by the tests. On each - // accepted launch (req_valid & req_ready) the emitted ND request is captured - // and enqueued with a retire-deadline; when its timer expires it is retired in - // order via the id gen so `done_id` advances. `busy`/`midend_busy` follow the - // outstanding count. A single-entry timer is enough — launches are retired FIFO - // and only re-armed once the previous one drains, which keeps ordering exact. + // Backend stub; req_ready is controllable so the tests can hold the grant off // -------------------------------------------------------------------------- idma_nd_req_t captured_q[$]; // every accepted launch, for self-check int unsigned outstanding; // in-flight (not yet retired) launches @@ -322,9 +307,7 @@ module tb_idma_reg_frontend import idma_pkg::*; import apb_test::apb_driver; #( end // -------------------------------------------------------------------------- - // Watchdog: fatal if a next_id APB read stays outstanding too long. - // `nxt_read_active` is raised by launch() around the blocking driver.read and - // cleared when it returns — a driver.read that never returns is caught here. + // Watchdog: fatal if a next_id read stays outstanding too long // -------------------------------------------------------------------------- logic nxt_read_active; int unsigned nxt_read_watchdog; @@ -347,9 +330,7 @@ module tb_idma_reg_frontend import idma_pkg::*; import apb_test::apb_driver; #( end // -------------------------------------------------------------------------- - // Global "launch accepted" pulse counter: an accepted launch is one arbiter - // handshake (issue). Used by the launch-integrity scoreboard and by the - // driver to wait for id-advance before re-launching. + // Accepted-launch counter: one arbiter handshake per launch // -------------------------------------------------------------------------- int unsigned launch_accept_count; int unsigned launch_acc_base; // accept-count snapshot taken at a launch read @@ -358,9 +339,7 @@ module tb_idma_reg_frontend import idma_pkg::*; import apb_test::apb_driver; #( else if (issue) launch_accept_count <= launch_accept_count + 1; end - // Test 5 (multi-port arbitration) scoreboard state. Each config port programs a - // unique src_addr that encodes its target stream, so the arbiter's presented - // winner (dma_req_o) can be mapped back to a stream and compared to stream_idx_o. + // Test 5 scoreboard: each port programs a src_addr encoding its identity logic [31:0] sb_addr_stream0; // src_addr programmed for stream 0's port logic [31:0] sb_addr_stream1; // src_addr programmed for stream 1's port int unsigned sb_mismatch; // times stream_idx != the winner's stream @@ -375,10 +354,7 @@ module tb_idma_reg_frontend import idma_pkg::*; import apb_test::apb_driver; #( drv[port].write(addr, data, '1, err); endtask - // next_id read via the driver, TIMED to recover the ACCESS-phase latency the - // non-blocking contract bounds. driver.read blocks until pready; measure the - // elapsed config clocks and subtract the fixed driver framing. The watchdog - // flag is held across the (blocking) call so a read that never returns fatals. + // next_id read, timed to recover the ACCESS-phase latency the contract bounds task automatic apb_read_next(input logic [31:0] addr, output logic [31:0] data, output int unsigned cyc, @@ -401,23 +377,20 @@ module tb_idma_reg_frontend import idma_pkg::*; import apb_test::apb_driver; #( input logic [31:0] len, input int unsigned port = 0); // conf: plain 1D incremental copy, ND disabled - apb_write(REG_CONF, 32'h0, port); - apb_write(REG_SRC_ADDR, src, port); - apb_write(REG_DST_ADDR, dst, port); - apb_write(REG_LENGTH, len, port); + apb_write(RegConf, 32'h0, port); + apb_write(RegSrcAddr, src, port); + apb_write(RegDstAddr, dst, port); + apb_write(RegLength, len, port); endtask - // launch: read next_id (the transfer trigger, non-blocking); returns id and the - // measured ACCESS-phase latency in `cyc`. Snapshots the accept count before the - // read so an accept coinciding with the (non-blocking) read is still observed. + // launch: read next_id and return the id with its measured latency task automatic launch(output logic [31:0] id, output int unsigned cyc, input int unsigned s = 0, input int unsigned port = 0); launch_acc_base = launch_accept_count; apb_read_next(reg_next_id(s), id, cyc, port); endtask - // wait until the launch read since the last launch() has been accepted (the - // arbiter grant / id-advance). SW confirms acceptance before re-launching. + // wait until the launch since the last launch() has been granted task automatic wait_launch_accepted(); int unsigned tries; tries = 0; @@ -523,9 +496,7 @@ module tb_idma_reg_frontend import idma_pkg::*; import apb_test::apb_driver; #( $display("[ ok ] Test1 done_id reached %0d", id0); // ------------------------------------------------------------------ - // Test 2 — Non-blocking read under backpressure (core gate) - // The next_id read MUST complete promptly (bounded latency) even while - // req_ready_i is held LOW. On a blocking template this FAILS. + // Test 2: the next_id read must complete under backend backpressure // ------------------------------------------------------------------ $display("\n--- Test 2: non-blocking read under backpressure ---"); backend_auto_retire = 1'b0; // no auto retire while we hold the stall @@ -556,9 +527,7 @@ module tb_idma_reg_frontend import idma_pkg::*; import apb_test::apb_driver; #( $display("[ ok ] Test2 completed after backpressure, block still live"); // ------------------------------------------------------------------ - // Test 2b — Launch integrity: a launch is never dropped when the grant - // is late. Hold req_ready low for several cycles AFTER a next_id read, - // then release; the latch must fire the launch EXACTLY ONCE. + // Test 2b: a launch is never dropped when the grant is late // ------------------------------------------------------------------ $display("\n--- Test 2b: launch integrity (late grant, no drop) ---"); backend_auto_retire = 1'b0; @@ -597,8 +566,7 @@ module tb_idma_reg_frontend import idma_pkg::*; import apb_test::apb_driver; #( $display("[ ok ] Test2b launch fired exactly once after late grant"); // ------------------------------------------------------------------ - // Test 3 — Multi-stream (only meaningful when NumStreams > 1) - // stream_idx_o must point at the launching stream until the grant lands. + // Test 3: stream_idx_o must point at the launching stream // ------------------------------------------------------------------ if (NumStreams > 1) begin int unsigned held1_cnt; // cycles stream_idx==1 while pending @@ -641,9 +609,7 @@ module tb_idma_reg_frontend import idma_pkg::*; import apb_test::apb_driver; #( end // ------------------------------------------------------------------ - // Test 4 — Back-to-back launches, monotonic ids, in-order done. - // Non-blocking: after each launch the driver waits for the id-advance - // (accept) before re-programming and re-launching. + // Test 4: back-to-back launches, monotonic ids, in-order done // ------------------------------------------------------------------ $display("\n--- Test 4: back-to-back launches ---"); backend_auto_retire = 1'b1; @@ -675,11 +641,7 @@ module tb_idma_reg_frontend import idma_pkg::*; import apb_test::apb_driver; #( end // ------------------------------------------------------------------ - // Test 5 — Concurrent multi-port launch: stream_idx must ride the - // arbitration (only meaningful when NumRegs>1 and NumStreams>1). - // Two config ports launch on *different* streams in the same window; on - // every grant stream_idx must match the ARBITRATED port's stream, not the - // last-pending port. This FAILS on the pre-fix RTL and PASSES after it. + // Test 5: stream_idx must ride the arbitration across ports // ------------------------------------------------------------------ if (NumRegs > 1 && NumStreams > 1) begin logic [31:0] id_p0, id_p1; @@ -694,18 +656,12 @@ module tb_idma_reg_frontend import idma_pkg::*; import apb_test::apb_driver; #( program_transfer(sb_addr_stream0, 32'hCCCC_0000, 32'h0000_0040, 0); program_transfer(sb_addr_stream1, 32'hDDDD_0000, 32'h0000_0080, 1); sb_mismatch = 0; - // launch both ports concurrently on different streams while the grant is held off, - // so both launch_pending latches are set at once (the arbiter must pick one). + // launch both ports concurrently so both latch before either is granted fork launch(id_p0, cyc0, 0, 0); // port 0, stream 0 launch(id_p1, cyc1, 1, 1); // port 1, stream 1 join - // Both launches are now pending with req_ready still low. rr_arb_tree (AxiVldRdy=1) - // *presents* its chosen winner on dma_req_o / idx_o even while the grant is withheld, - // so stream_idx_o must equal the winner's stream. On the pre-fix RTL stream_idx_o is - // the last-pending port's stream (held_stream[NumRegs-1]) regardless of the winner, - // so it disagrees with dma_req_o whenever the winner is not the last port. Check the - // presented (winner, stream_idx) pair across the whole held window. + // rr_arb_tree presents its winner before the grant, so sample on the handshake begin int unsigned held_checks; held_checks = 0; @@ -775,8 +731,7 @@ module tb_idma_reg_frontend import idma_pkg::*; import apb_test::apb_driver; #( $finish; end - // global safety net: kill a run that hangs entirely (belt-and-braces with the - // per-read watchdog, in case a hang happens outside a tracked next_id read). + // global safety net: kill a run that hangs outside the per-read watchdog initial begin #(TCK * 200000); $fatal(1, "GLOBAL TIMEOUT: testbench did not finish"); diff --git a/test/future/idma_obi2axi_bridge.sv b/test/future/idma_obi2axi_bridge.sv index 5acd9a01..52b6e69a 100644 --- a/test/future/idma_obi2axi_bridge.sv +++ b/test/future/idma_obi2axi_bridge.sv @@ -67,7 +67,7 @@ module idma_obi2axi_bridge #( .axi_master_aw_qos_o ( axi_req_o.aw.qos ), .axi_master_aw_id_o ( axi_req_o.aw.id ), .axi_master_aw_user_o ( axi_req_o.aw.user ), - .axi_master_aw_ready_i ( axi_rsp_i.aw_ready ), + .axi_master_aw_ready_i ( axi_rsp_i.aw_ready ), .axi_master_ar_valid_o ( axi_req_o.ar_valid ), .axi_master_ar_addr_o ( axi_req_o.ar.addr ), @@ -81,7 +81,7 @@ module idma_obi2axi_bridge #( .axi_master_ar_qos_o ( axi_req_o.ar.qos ), .axi_master_ar_id_o ( axi_req_o.ar.id ), .axi_master_ar_user_o ( axi_req_o.ar.user ), - .axi_master_ar_ready_i ( axi_rsp_i.ar_ready ), + .axi_master_ar_ready_i ( axi_rsp_i.ar_ready ), .axi_master_w_valid_o ( axi_req_o.w_valid ), .axi_master_w_data_o ( axi_req_o.w.data ), diff --git a/test/future/idma_tilelink2axi_bridge.sv b/test/future/idma_tilelink2axi_bridge.sv index 8ab073c6..5612e041 100644 --- a/test/future/idma_tilelink2axi_bridge.sv +++ b/test/future/idma_tilelink2axi_bridge.sv @@ -7,7 +7,7 @@ `include "idma/guard.svh" module idma_tilelink2axi_bridge #( - parameter int unsigned DataWidth = 64, + parameter int unsigned DataWidth = 64, parameter int unsigned AddrWidth = 32, parameter int unsigned IdWidth = 7, parameter type tilelink_req_t = logic, diff --git a/test/idma_test.sv b/test/idma_test.sv index 899b8433..ba233cd1 100644 --- a/test/idma_test.sv +++ b/test/idma_test.sv @@ -52,14 +52,15 @@ package idma_test; idma_pkg::idma_eh_req_t err_action [$]; // format string for pretty printing - string format = "\n-----------------------------------------------\ - \niDMA %1dD job:\n num_bytes: %d\ - \n src: %s\n dst: %s\ - \n src_protocol: %s%d\n dst_protocol: %s%d\ - \n max_src_len: %s%d\n max_dst_len: %s%d\ - \n aw_decoupled: %s%b\n rw_decoupled: %s%b\ - \n id: %s%d \n%s errors:\n%s\ - \n-----------------------------------------------"; + string format = { + "\n-----------------------------------------------", + " \niDMA %1dD job:\n num_bytes: %d", + " \n src: %s\n dst: %s", + " \n src_protocol: %s%d\n dst_protocol: %s%d", + " \n max_src_len: %s%d\n max_dst_len: %s%d", + " \n aw_decoupled: %s%b\n rw_decoupled: %s%b", + " \n id: %s%d \n%s errors:\n%s", + " \n-----------------------------------------------"}; // constructor: create an empty job function new (); @@ -108,8 +109,10 @@ package idma_test; for (int d = 0; d < NumDim-1; d++) begin res = {res, $sformatf(" Dimension %2d: \n", d+2)}; res = {res, $sformatf(" reps: %d", n_dims[d].reps), "\n"}; - res = {res, $sformatf(" src stride: %s", format_hex(n_dims[d].src_strides)), "\n"}; - res = {res, $sformatf(" dst stride: %s", format_hex(n_dims[d].dst_strides)), "\n"}; + res = {res, $sformatf(" src stride: %s", + format_hex(n_dims[d].src_strides)), "\n"}; + res = {res, $sformatf(" dst stride: %s", + format_hex(n_dims[d].dst_strides)), "\n"}; end return res; endfunction @@ -407,8 +410,7 @@ package idma_test; write_action, last_w_err_len); end - // how error is handled - // no error + // how error is handled no error if (!aborted & !read_error & !write_error) begin temp = read_byte(src_ptr, src_protocol); write_byte(temp, dst_ptr, dst_protocol); @@ -436,17 +438,6 @@ package idma_test; aborted = 1; temp = 'x; end - // // replay (not implemented in hardware ) - // if (read_error && read_action === idma_pkg::REPLAY) begin - // temp = read_byte(src_ptr); - // if (ModelOutput) - // $display("Eventually read %h from 0x%h", temp, src_ptr); - // end - // if (write_error && write_action === idma_pkg::REPLAY) begin - // if (ModelOutput) - // $display("Eventually write %h to 0x%h", temp, dst_ptr); - // end - // aborted if (aborted) begin if (ModelOutput) begin $display("Omitted read from 0x%h", src_ptr); @@ -466,8 +457,9 @@ package idma_test; // abort all further transfers aborted = 1; if (ModelOutput) - $display("Aborting all further elements of this \ - transfer due to write error"); + $display({"Aborting all further elements of this ", + " ", + "transfer due to write error"}); end // read abort logic @@ -476,8 +468,9 @@ package idma_test; // abort all further transfers aborted = 1; if (ModelOutput) - $display("Aborting all further elements of this \ - transfer due to read error"); + $display({"Aborting all further elements of this ", + " ", + "transfer due to read error"}); end // debug print tail @@ -538,8 +531,7 @@ package idma_test; end end - // decompose - // at least one dimension must be active + // decompose at least one dimension must be active if (active != '0) begin // while not done while (!done) begin @@ -577,8 +569,7 @@ package idma_test; done = 1; // the higher dimensions end else if(active[d]) begin - // if the counter below is 0: decrement and reset lower counter - // and add stride + // when the lower counter hits 0: reset it and add the stride if (counters[d-1] == '0) begin counters[d] = counters[d] - 1; counters[d-1] = nd_job.n_dims[d-1].reps; @@ -710,8 +701,9 @@ package idma_test; idma.req.opt.beo.src_reduce_len <= #TA src_reduce_len; idma.req.opt.beo.dst_reduce_len <= #TA dst_reduce_len; idma.req.opt.compute.enable <= #TA transpose_en; - idma.req.opt.compute.op <= #TA transpose_en ? idma_pkg::COMPUTE_TRANSPOSE - : idma_pkg::COMPUTE_NONE; + idma.req.opt.compute.op <= #TA transpose_en + ? idma_pkg::COMPUTE_TRANSPOSE + : idma_pkg::COMPUTE_NONE; idma.req.opt.compute.params.transpose.mode <= #TA transp_mode; idma.req.opt.compute.params.transpose.tensor_m <= #TA tensor_m; idma.req.opt.compute.params.transpose.tensor_n <= #TA tensor_n; diff --git a/test/include/tb_tasks.svh b/test/include/tb_tasks.svh index 62735e12..65bb0a9e 100644 --- a/test/include/tb_tasks.svh +++ b/test/include/tb_tasks.svh @@ -8,7 +8,7 @@ // write a byte to the AXI-attached memory `ifdef PROT_AXI4 - task write_byte_axi_mem ( + task automatic write_byte_axi_mem ( input byte_t byte_i, input addr_t addr_i ); @@ -16,7 +16,7 @@ endtask // read a byte from the AXI-attached memory - task read_byte_axi_mem ( + task automatic read_byte_axi_mem ( output byte_t byte_o, input addr_t addr_i ); @@ -26,7 +26,7 @@ byte_o = '1; endtask `else - task write_byte_axi_mem ( + task automatic write_byte_axi_mem ( input byte_t byte_i, input addr_t addr_i ); @@ -34,7 +34,7 @@ endtask // read a byte from the AXI-attached memory - task read_byte_axi_mem ( + task automatic read_byte_axi_mem ( output byte_t byte_o, input addr_t addr_i ); @@ -45,7 +45,7 @@ `ifdef PROT_AXI4_LITE // write a byte to the AXI-Lite AXI-attached memory - task write_byte_axi_lite_axi_mem ( + task automatic write_byte_axi_lite_axi_mem ( input byte_t byte_i, input addr_t addr_i ); @@ -53,7 +53,7 @@ endtask // read a byte from the AXI-Lite AXI-attached memory - task read_byte_axi_lite_axi_mem ( + task automatic read_byte_axi_lite_axi_mem ( output byte_t byte_o, input addr_t addr_i ); @@ -64,7 +64,7 @@ endtask `else // write a byte to the AXI-Lite AXI-attached memory - task write_byte_axi_lite_axi_mem ( + task automatic write_byte_axi_lite_axi_mem ( input byte_t byte_i, input addr_t addr_i ); @@ -72,7 +72,7 @@ endtask // read a byte from the AXI-Lite AXI-attached memory - task read_byte_axi_lite_axi_mem ( + task automatic read_byte_axi_lite_axi_mem ( output byte_t byte_o, input addr_t addr_i ); @@ -83,7 +83,7 @@ `ifdef PROT_OBI // seed a byte into the OBI source (read) memory - task write_byte_obi_axi_mem ( + task automatic write_byte_obi_axi_mem ( input byte_t byte_i, input addr_t addr_i ); @@ -91,7 +91,7 @@ endtask // read a byte back from the OBI destination (write) memory - task read_byte_obi_axi_mem ( + task automatic read_byte_obi_axi_mem ( output byte_t byte_o, input addr_t addr_i ); @@ -102,7 +102,7 @@ endtask `else // write a byte to the OBI AXI-attached memory - task write_byte_obi_axi_mem ( + task automatic write_byte_obi_axi_mem ( input byte_t byte_i, input addr_t addr_i ); @@ -110,7 +110,7 @@ endtask // read a byte from the OBI AXI-attached memory - task read_byte_obi_axi_mem ( + task automatic read_byte_obi_axi_mem ( output byte_t byte_o, input addr_t addr_i ); @@ -121,7 +121,7 @@ `ifdef PROT_TILELINK // write a byte to the TileLink AXI-attached memory - task write_byte_tilelink_axi_mem ( + task automatic write_byte_tilelink_axi_mem ( input byte_t byte_i, input addr_t addr_i ); @@ -129,7 +129,7 @@ endtask // read a byte from the TileLink AXI-attached memory - task read_byte_tilelink_axi_mem ( + task automatic read_byte_tilelink_axi_mem ( output byte_t byte_o, input addr_t addr_i ); @@ -140,7 +140,7 @@ endtask `else // write a byte to the TileLink AXI-attached memory - task write_byte_tilelink_axi_mem ( + task automatic write_byte_tilelink_axi_mem ( input byte_t byte_i, input addr_t addr_i ); @@ -148,7 +148,7 @@ endtask // read a byte from the TileLink AXI-attached memory - task read_byte_tilelink_axi_mem ( + task automatic read_byte_tilelink_axi_mem ( output byte_t byte_o, input addr_t addr_i ); @@ -159,7 +159,7 @@ `ifdef PROT_AXI4_STREAM // write a byte to the AXI Stream AXI-attached memory - task write_byte_axis_axi_mem ( + task automatic write_byte_axis_axi_mem ( input byte_t byte_i, input addr_t addr_i ); @@ -167,7 +167,7 @@ endtask // read a byte from the AXI Stream AXI-attached memory - task read_byte_axis_axi_mem ( + task automatic read_byte_axis_axi_mem ( output byte_t byte_o, input addr_t addr_i ); @@ -178,7 +178,7 @@ endtask `else // write a byte to the AXI Stream AXI-attached memory - task write_byte_axis_axi_mem ( + task automatic write_byte_axis_axi_mem ( input byte_t byte_i, input addr_t addr_i ); @@ -186,7 +186,7 @@ endtask // read a byte from the AXI Stream AXI-attached memory - task read_byte_axis_axi_mem ( + task automatic read_byte_axis_axi_mem ( output byte_t byte_o, input addr_t addr_i ); @@ -197,7 +197,7 @@ `ifdef PROT_AXI4 // set error flag in the AXI-attached memory - task set_error_mem ( + task automatic set_error_mem ( input addr_t addr_i, input logic is_read_i, input axi_pkg::resp_t resp_i @@ -209,7 +209,7 @@ endtask `else // set error flag in the AXI-attached memory - task set_error_mem ( + task automatic set_error_mem ( input addr_t addr_i, input logic is_read_i, input axi_pkg::resp_t resp_i @@ -219,7 +219,7 @@ `endif // compare if a range of bytes matches - task compare_mem ( + task automatic compare_mem ( input addr_t length_i, input addr_t addr_i, input idma_pkg::protocol_e protocol, @@ -235,7 +235,7 @@ while (now < length_i) begin case(protocol) idma_pkg::AXI: read_byte_axi_mem (data, addr_i + now); - idma_pkg::AXILITE: read_byte_axi_lite_axi_mem (data, addr_i + now); + idma_pkg::AXILITE: read_byte_axi_lite_axi_mem (data, addr_i + now); idma_pkg::OBI: read_byte_obi_axi_mem (data, addr_i + now); idma_pkg::TILELINK: read_byte_tilelink_axi_mem (data, addr_i + now); idma_pkg::AXI_STREAM: read_byte_axis_axi_mem(data, addr_i + now); @@ -262,11 +262,15 @@ // global match flag for the burst match_o = match_o & local_match & !local_x; if (Debug) - $display("[tb ] compare: %h - %h @0x%h - (idma - model - addr) - match: %b", data, model_byte, addr_i + now, local_match); + $display("[tb ] compare: %h - %h @0x%h - (idma - model - addr) - match: %b", + data, model_byte, addr_i + now, local_match); if (!local_match) - $display("[tb ] mismatch: %h - %h @0x%h - (idma - model - addr) - match: %b", data, model_byte, addr_i + now, local_match); + $display("[tb ] mismatch: %h - %h @0x%h - (idma - model - addr) - match: %b", + data, model_byte, addr_i + now, local_match); if (local_x) - $display("[tb ] idma has x: %h (%b) - %h @0x%h - (idma - model - addr) - match: %b", data, data, model_byte, addr_i + now, local_match); + $display( + "[tb ] idma has x: %h (%b) - %h @0x%h - (idma - model - addr) - match: %b", + data, data, model_byte, addr_i + now, local_match); now++; end endtask @@ -282,12 +286,12 @@ axi_pkg::resp_t cause; addr_t burst_addr; int err_idx [$]; - // multiple errors can happen -> once one occurs - // handle it after checking the list + // multiple errors can happen -> once one occurs handle it after checking the list while (1) begin drv.wait_tf(cause, err_type, burst_addr, error, last); // if bus error occurs - if (error & (err_type == idma_pkg::BUS_READ | err_type == idma_pkg::BUS_WRITE) & ErrorCap == idma_pkg::ERROR_HANDLING) begin + if (error & (err_type == idma_pkg::BUS_READ | err_type == idma_pkg::BUS_WRITE) + & ErrorCap == idma_pkg::ERROR_HANDLING) begin err_idx = now_r.err_addr.find_first_index with (item == burst_addr); // handle it drv.handle_error(now_r.err_action[err_idx[0]]); @@ -318,7 +322,8 @@ write_byte_axi_mem ( to_write, now_r.src_addr + now); end idma_pkg::AXILITE: begin - model.write_byte ( -to_write, now_r.src_addr + now, idma_pkg::AXILITE); + model.write_byte ( -to_write, now_r.src_addr + now, + idma_pkg::AXILITE); write_byte_axi_lite_axi_mem ( -to_write, now_r.src_addr + now); end idma_pkg::OBI: begin @@ -326,15 +331,19 @@ write_byte_obi_axi_mem ( ~to_write, now_r.src_addr + now); end idma_pkg::TILELINK: begin - model.write_byte ( {to_write[3:0], to_write[7:4]}, now_r.src_addr + now, idma_pkg::TILELINK ); - write_byte_tilelink_axi_mem ( {to_write[3:0], to_write[7:4]}, now_r.src_addr + now ); + model.write_byte ( {to_write[3:0], to_write[7:4]}, + now_r.src_addr + now, idma_pkg::TILELINK ); + write_byte_tilelink_axi_mem ( {to_write[3:0], to_write[7:4]}, + now_r.src_addr + now ); end idma_pkg::INIT: begin model.write_byte ( 8'h42, now_r.src_addr + now, idma_pkg::INIT ); end idma_pkg::AXI_STREAM: begin - model.write_byte ( ~{to_write[3:0], to_write[7:4]}, now_r.src_addr + now, idma_pkg::AXI_STREAM ); - write_byte_axis_axi_mem ( ~{to_write[3:0], to_write[7:4]}, now_r.src_addr + now ); + model.write_byte ( ~{to_write[3:0], to_write[7:4]}, + now_r.src_addr + now, idma_pkg::AXI_STREAM ); + write_byte_axis_axi_mem ( ~{to_write[3:0], to_write[7:4]}, + now_r.src_addr + now ); end default: $fatal(1, "init_mem not implemented for used protocol!"); endcase diff --git a/test/midend/tb_idma_nd_midend.sv b/test/midend/tb_idma_nd_midend.sv index e0e1a240..5f42d08e 100644 --- a/test/midend/tb_idma_nd_midend.sv +++ b/test/midend/tb_idma_nd_midend.sv @@ -49,8 +49,7 @@ module tb_idma_nd_midend import idma_pkg::*; #( localparam bit ModelOutput = 1'b0; localparam bit PrintFifoInfo = 1'b1; - // TB parameters - // dependent parameters + // TB parameters dependent parameters localparam int unsigned StrbWidth = DataWidth / 8; localparam int unsigned OffsetWidth = $clog2(StrbWidth); @@ -332,22 +331,34 @@ module tb_idma_nd_midend import idma_pkg::*; #( // TB Monitors //-------------------------------------- // AXI - signal_highlighter #(.T(axi_aw_chan_t)) i_aw_hl (.ready_i(axi_rsp.aw_ready), .valid_i(axi_req.aw_valid), .data_i(axi_req.aw)); - signal_highlighter #(.T(axi_ar_chan_t)) i_ar_hl (.ready_i(axi_rsp.ar_ready), .valid_i(axi_req.ar_valid), .data_i(axi_req.ar)); - signal_highlighter #(.T(axi_w_chan_t)) i_w_hl (.ready_i(axi_rsp.w_ready), .valid_i(axi_req.w_valid), .data_i(axi_req.w)); - signal_highlighter #(.T(axi_r_chan_t)) i_r_hl (.ready_i(axi_req.r_ready), .valid_i(axi_rsp.r_valid), .data_i(axi_rsp.r)); - signal_highlighter #(.T(axi_b_chan_t)) i_b_hl (.ready_i(axi_req.b_ready), .valid_i(axi_rsp.b_valid), .data_i(axi_rsp.b)); + signal_highlighter #(.T(axi_aw_chan_t)) i_aw_hl ( + .ready_i(axi_rsp.aw_ready), .valid_i(axi_req.aw_valid), .data_i(axi_req.aw)); + signal_highlighter #(.T(axi_ar_chan_t)) i_ar_hl ( + .ready_i(axi_rsp.ar_ready), .valid_i(axi_req.ar_valid), .data_i(axi_req.ar)); + signal_highlighter #(.T(axi_w_chan_t)) i_w_hl ( + .ready_i(axi_rsp.w_ready), .valid_i(axi_req.w_valid), .data_i(axi_req.w)); + signal_highlighter #(.T(axi_r_chan_t)) i_r_hl ( + .ready_i(axi_req.r_ready), .valid_i(axi_rsp.r_valid), .data_i(axi_rsp.r)); + signal_highlighter #(.T(axi_b_chan_t)) i_b_hl ( + .ready_i(axi_req.b_ready), .valid_i(axi_rsp.b_valid), .data_i(axi_rsp.b)); // DMA backend types - signal_highlighter #(.T(idma_nd_req_t)) i_nd_req_hl (.ready_i(nd_req_ready), .valid_i(nd_req_valid), .data_i(nd_req)); - signal_highlighter #(.T(idma_rsp_t)) i_nd_rsp_hl (.ready_i(nd_rsp_ready), .valid_i(nd_rsp_valid), .data_i(nd_rsp)); - signal_highlighter #(.T(idma_req_t)) i_req_hl (.ready_i(burst_req_ready), .valid_i(burst_req_valid), .data_i(burst_req)); - signal_highlighter #(.T(idma_rsp_t)) i_rsp_hl (.ready_i(burst_rsp_ready), .valid_i(burst_rsp_valid), .data_i(burst_rsp)); - signal_highlighter #(.T(idma_eh_req_t)) i_eh_hl (.ready_i(eh_req_ready), .valid_i(eh_req_valid), .data_i(idma_eh_req)); + signal_highlighter #(.T(idma_nd_req_t)) i_nd_req_hl ( + .ready_i(nd_req_ready), .valid_i(nd_req_valid), .data_i(nd_req)); + signal_highlighter #(.T(idma_rsp_t)) i_nd_rsp_hl ( + .ready_i(nd_rsp_ready), .valid_i(nd_rsp_valid), .data_i(nd_rsp)); + signal_highlighter #(.T(idma_req_t)) i_req_hl ( + .ready_i(burst_req_ready), .valid_i(burst_req_valid), .data_i(burst_req)); + signal_highlighter #(.T(idma_rsp_t)) i_rsp_hl ( + .ready_i(burst_rsp_ready), .valid_i(burst_rsp_valid), .data_i(burst_rsp)); + signal_highlighter #(.T(idma_eh_req_t)) i_eh_hl ( + .ready_i(eh_req_ready), .valid_i(eh_req_valid), .data_i(idma_eh_req)); // Watchdogs - stream_watchdog #(.NumCycles(WatchDogNumCycles)) i_axi_w_watchdog (.clk_i(clk), .rst_ni(rst_n), .valid_i(axi_req.w_valid), .ready_i(axi_rsp.w_ready)); - stream_watchdog #(.NumCycles(WatchDogNumCycles)) i_axi_r_watchdog (.clk_i(clk), .rst_ni(rst_n), .valid_i(axi_rsp.r_valid), .ready_i(axi_req.r_ready)); + stream_watchdog #(.NumCycles(WatchDogNumCycles)) i_axi_w_watchdog ( + .clk_i(clk), .rst_ni(rst_n), .valid_i(axi_req.w_valid), .ready_i(axi_rsp.w_ready)); + stream_watchdog #(.NumCycles(WatchDogNumCycles)) i_axi_r_watchdog ( + .clk_i(clk), .rst_ni(rst_n), .valid_i(axi_rsp.r_valid), .ready_i(axi_req.r_ready)); //-------------------------------------- @@ -428,7 +439,7 @@ module tb_idma_nd_midend import idma_pkg::*; #( // DMA Tracer //-------------------------------------- // only activate tracer if requested - if (DmaTracing) begin + if (DmaTracing) begin : gen_dma_tracer // fetch the name of the trace file from CMD line string trace_file; initial begin diff --git a/test/midend/tb_idma_nd_midend_b2b.sv b/test/midend/tb_idma_nd_midend_b2b.sv index e849a1e0..089ec5b2 100644 --- a/test/midend/tb_idma_nd_midend_b2b.sv +++ b/test/midend/tb_idma_nd_midend_b2b.sv @@ -54,13 +54,14 @@ module tb_idma_nd_midend_b2b; .clk_i(clk), .rst_ni(rst_n), .nd_req_i(nd_req), .nd_req_valid_i(nd_req_valid), .nd_req_ready_o(nd_req_ready), .nd_rsp_o(nd_rsp), .nd_rsp_valid_o(nd_rsp_valid), .nd_rsp_ready_i(nd_rsp_ready), - .burst_req_o(burst_req), .burst_req_valid_o(burst_req_valid), .burst_req_ready_i(burst_req_ready), - .burst_rsp_i(burst_rsp), .burst_rsp_valid_i(burst_rsp_valid), .burst_rsp_ready_o(burst_rsp_ready), + .burst_req_o(burst_req), .burst_req_valid_o(burst_req_valid), + .burst_req_ready_i(burst_req_ready), + .burst_rsp_i(burst_rsp), .burst_rsp_valid_i(burst_rsp_valid), + .burst_rsp_ready_o(burst_rsp_ready), .busy_o(busy) ); - // Backpressure on burst_req_ready is essential: during a stall stride_sel_q - // collapses toward 0, which is what can defeat the base reload. ready always-1 hides it. + // Backpressure is essential: stride_sel_q collapses toward 0 during a stall logic [2:0] bp_lfsr; always @(posedge clk or negedge rst_n) if (!rst_n) bp_lfsr <= 3'b101; @@ -124,19 +125,41 @@ module tb_idma_nd_midend_b2b; begin errs++; $display("[B2B] burst count %0d != %0d", cap_src.size(), 3*NB); end else begin // first burst of each transfer must equal its OWN base (reload happened) - if (cap_src[0] !== S1 || cap_dst[0] !== D1) begin errs++; $display("[B2B] T1[0]=(%0h,%0h) exp (%0h,%0h)", cap_src[0], cap_dst[0], S1, D1); end - if (cap_src[NB] !== S2 || cap_dst[NB] !== D2) begin errs++; $display("[B2B] T2[0]=(%0h,%0h) exp (%0h,%0h) -- back-to-back base NOT reloaded", cap_src[NB], cap_dst[NB], S2, D2); end - if (cap_src[2*NB] !== S3 || cap_dst[2*NB] !== D3) begin errs++; $display("[B2B] T3[0]=(%0h,%0h) exp (%0h,%0h)", cap_src[2*NB], cap_dst[2*NB], S3, D3); end + if (cap_src[0] !== S1 || cap_dst[0] !== D1) begin + errs++; + $display("[B2B] T1[0]=(%0h,%0h) exp (%0h,%0h)", cap_src[0], cap_dst[0], S1, D1); + end + if (cap_src[NB] !== S2 || cap_dst[NB] !== D2) begin + errs++; + $display("[B2B] T2[0]=(%0h,%0h) exp (%0h,%0h) -- back-to-back base NOT reloaded", + cap_src[NB], cap_dst[NB], S2, D2); + end + if (cap_src[2*NB] !== S3 || cap_dst[2*NB] !== D3) begin + errs++; + $display("[B2B] T3[0]=(%0h,%0h) exp (%0h,%0h)", cap_src[2*NB], cap_dst[2*NB], S3, D3); + end // full-sequence independence: T2 and T3 must be T1 shifted by their base delta for (int unsigned i = 0; i < NB; i++) begin - if ((cap_src[NB+i] - cap_src[i]) !== (S2 - S1) || (cap_dst[NB+i] - cap_dst[i]) !== (D2 - D1)) begin - errs++; if (errs <= 8) $display("[B2B] T2[%0d] not T1+delta: src %0h vs %0h (Δexp %0h)", i, cap_src[NB+i], cap_src[i], S2-S1); end - if ((cap_src[2*NB+i] - cap_src[i]) !== (S3 - S1) || (cap_dst[2*NB+i] - cap_dst[i]) !== (D3 - D1)) begin - errs++; if (errs <= 8) $display("[B2B] T3[%0d] not T1+delta: src %0h vs %0h (Δexp %0h)", i, cap_src[2*NB+i], cap_src[i], S3-S1); end + if ((cap_src[NB+i] - cap_src[i]) !== (S2 - S1) || + (cap_dst[NB+i] - cap_dst[i]) !== (D2 - D1)) begin + errs++; + if (errs <= 8) + $display("[B2B] T2[%0d] not T1+delta: src %0h vs %0h (Δexp %0h)", + i, cap_src[NB+i], cap_src[i], S2-S1); + end + if ((cap_src[2*NB+i] - cap_src[i]) !== (S3 - S1) || + (cap_dst[2*NB+i] - cap_dst[i]) !== (D3 - D1)) begin + errs++; + if (errs <= 8) + $display("[B2B] T3[%0d] not T1+delta: src %0h vs %0h (Δexp %0h)", + i, cap_src[2*NB+i], cap_src[i], S3-S1); + end end end - if (errs == 0) $display("[B2B] PASS: %0d back-to-back + gapped ND transfers each walked from their own base", 3*NB); + if (errs == 0) + $display("[B2B] PASS: %0d back-to-back + gapped ND transfers each walked from their own base", + 3*NB); else $fatal(1, "[B2B] FAIL: %0d errors (back-to-back ND base-address reuse)", errs); $finish(); end diff --git a/test/tb_idma_backend_multihead.sv b/test/tb_idma_backend_multihead.sv index 02bce894..e0b3ed2b 100644 --- a/test/tb_idma_backend_multihead.sv +++ b/test/tb_idma_backend_multihead.sv @@ -10,8 +10,7 @@ // its own axi_sim_mem so read-head routing is observable: src_head selects which // read memory a transfer sources from, while all writes land in the single write // memory. Preloading distinct data per read head and checking the write memory -// catches a backend that ignores or mis-routes src_head. Uses a per-head -// memory harness pattern. +// catches a backend that ignores or mis-routes src_head. Uses a per-head memory harness pattern. `timescale 1ns/1ns `include "axi/typedef.svh" @@ -96,8 +95,7 @@ module tb_idma_backend_multihead import idma_pkg::*; #( assign idma_req = idma_dv.req; assign req_valid = idma_dv.req_valid; - // TB never inspects the completion response, only the written memory; keep - // rsp_ready asserted so the AXI read datapath can drain R into the buffer. + // rsp_ready stays high so the read datapath can drain assign rsp_ready = 1'b1; assign idma_eh_req = idma_dv.eh_req; assign eh_req_valid = idma_dv.eh_req_valid; @@ -155,7 +153,8 @@ module tb_idma_backend_multihead import idma_pkg::*; #( .clk_i (clk), .rst_ni (rst_n), .idma_req_i (idma_req), .req_valid_i (req_valid), .req_ready_o (req_ready), .idma_rsp_o (idma_rsp), .rsp_valid_o (rsp_valid), .rsp_ready_i (rsp_ready), - .idma_eh_req_i (idma_eh_req), .eh_req_valid_i (eh_req_valid), .eh_req_ready_o (eh_req_ready), + .idma_eh_req_i (idma_eh_req), .eh_req_valid_i (eh_req_valid), + .eh_req_ready_o (eh_req_ready), .axi_read_req_o (axi_read_req), .axi_read_rsp_i (axi_read_rsp), .axi_write_req_o (axi_write_req), .axi_write_rsp_i (axi_write_rsp), .busy_o (busy) @@ -225,8 +224,7 @@ module tb_idma_backend_multihead import idma_pkg::*; #( do_copy(LEN, 32'h0000_1000, 32'h0000_2000, 0, 'd1); check("rhead0", 32'h0000_2000, LEN, s0); - // read head 1 -> write memory: data must be head-1's pattern. - // If src_head routing is broken, this reads head 0 and the check fails. + // read head 1: broken src_head routing would read head 0 instead do_copy(LEN, 32'h0000_1000, 32'h0000_3000, 1, 'd2); check("rhead1", 32'h0000_3000, LEN, s1); diff --git a/test/tb_idma_backend_multihead_rw.sv b/test/tb_idma_backend_multihead_rw.sv index 47049de2..dfad094e 100644 --- a/test/tb_idma_backend_multihead_rw.sv +++ b/test/tb_idma_backend_multihead_rw.sv @@ -93,8 +93,7 @@ module tb_idma_backend_multihead_rw import idma_pkg::*; #( assign idma_req = idma_dv.req; assign req_valid = idma_dv.req_valid; - // TB only inspects the written memory; keep rsp_ready asserted so the read - // datapath can drain R into the buffer. + // rsp_ready stays high so the read datapath can drain assign rsp_ready = 1'b1; assign idma_eh_req = idma_dv.eh_req; assign eh_req_valid = idma_dv.eh_req_valid; @@ -146,7 +145,8 @@ module tb_idma_backend_multihead_rw import idma_pkg::*; #( .clk_i (clk), .rst_ni (rst_n), .idma_req_i (idma_req), .req_valid_i (req_valid), .req_ready_o (req_ready), .idma_rsp_o (idma_rsp), .rsp_valid_o (rsp_valid), .rsp_ready_i (rsp_ready), - .idma_eh_req_i (idma_eh_req), .eh_req_valid_i (eh_req_valid), .eh_req_ready_o (eh_req_ready), + .idma_eh_req_i (idma_eh_req), .eh_req_valid_i (eh_req_valid), + .eh_req_ready_o (eh_req_ready), .axi_read_req_o (axi_read_req), .axi_read_rsp_i (axi_read_rsp), .axi_write_req_o (axi_write_req), .axi_write_rsp_i (axi_write_rsp), .busy_o (busy) @@ -168,8 +168,10 @@ module tb_idma_backend_multihead_rw import idma_pkg::*; #( function automatic byte_t mem_get (input int unsigned head, input addr_t a); case (head) - 0: return gen_head[0].i_axi_sim_mem.mem.exists(a) ? gen_head[0].i_axi_sim_mem.mem[a] : 8'hxx; - 1: return gen_head[1].i_axi_sim_mem.mem.exists(a) ? gen_head[1].i_axi_sim_mem.mem[a] : 8'hxx; + 0: return gen_head[0].i_axi_sim_mem.mem.exists(a) + ? gen_head[0].i_axi_sim_mem.mem[a] : 8'hxx; + 1: return gen_head[1].i_axi_sim_mem.mem.exists(a) + ? gen_head[1].i_axi_sim_mem.mem[a] : 8'hxx; default: return 8'hxx; endcase endfunction @@ -224,8 +226,7 @@ module tb_idma_backend_multihead_rw import idma_pkg::*; #( do_copy(LEN, 32'h0000_1000, 32'h0000_2000, 1, 1, 'd2); check("same1", 1, 32'h0000_2000, LEN, s1); - // cross-head: read head 0, write head 1 -> data must land in head 1's memory. - // With the dst_head<-src_head bug it lands in head 0 instead, so this fails. + // cross-head: the dst_head<-src_head bug lands the data in head 0 do_copy(LEN, 32'h0000_1000, 32'h0000_3000, 0, 1, 'd3); check("cross0to1", 1, 32'h0000_3000, LEN, s0); diff --git a/test/tb_idma_mxquant.sv b/test/tb_idma_mxquant.sv index 50da4c1e..f302d941 100644 --- a/test/tb_idma_mxquant.sv +++ b/test/tb_idma_mxquant.sv @@ -92,6 +92,8 @@ module tb_idma_mxquant 5: return 32'h7FC1_2345; 6: return 32'h7F7F_FFFF; 7: return 32'h0080_0000; + // unreachable: e % 8 covers 0-7; a sentinel fails the byte-exact compare + default: return 32'hDEAD_BEEF; endcase end return 32'((e & 1) << 31) | 32'(((64 + (e % 128)) & 8'hFF) << 23) diff --git a/test/tb_idma_mxrand.sv b/test/tb_idma_mxrand.sv index b8d31af8..aae468b1 100644 --- a/test/tb_idma_mxrand.sv +++ b/test/tb_idma_mxrand.sv @@ -134,7 +134,7 @@ module tb_idma_mxrand initial begin automatic int unsigned errs = 0; - automatic logic [7:0] golden [0:1023]; + automatic logic [7:0] golden [1024]; automatic logic [15:0] h; automatic logic [31:0] w; automatic addr_t src, dst; diff --git a/test/tb_idma_otf_transpose.sv b/test/tb_idma_otf_transpose.sv index 86ebc80a..f1b2c84a 100644 --- a/test/tb_idma_otf_transpose.sv +++ b/test/tb_idma_otf_transpose.sv @@ -9,7 +9,7 @@ // M x N transpose of EB-byte elements (EB in {1,2,4}) against the DPI-C golden // (idma_transpose_dpi.c). Sweeps a geometry list internally; M/N/EB are runtime // DUT inputs, so one elaboration covers all geometries for a given StrbWidth/ -// FullDuplex. Optional backpressure via +BP. +// FullDuplex. Optional backpressure via +BP=1. `timescale 1ns/1ps @@ -26,7 +26,8 @@ module tb_idma_otf_transpose #( // Geometry cases (M, N, EB); EB>StrbWidth cases skip. localparam int unsigned NCases = 4; - localparam int unsigned Cases [NCases][3] = '{ '{13, 19, 1}, '{7, 5, 2}, '{5, 3, 4}, '{130, 70, 1} }; + localparam int unsigned Cases [NCases][3] = '{ '{13, 19, 1}, '{7, 5, 2}, + '{5, 3, 4}, '{130, 70, 1} }; logic clk = 1'b0, rst_n = 1'b0, clear = 1'b0; always #5 clk = ~clk; @@ -65,7 +66,9 @@ module tb_idma_otf_transpose #( int unsigned errors = 0; // running total across all cases bit backpressure = 1'b0; - task automatic drive_inputs(input int unsigned m, n, eb, ne, yt, nt); + task automatic drive_inputs(input int unsigned m, input int unsigned n, + input int unsigned eb, input int unsigned ne, + input int unsigned yt, input int unsigned nt); int unsigned beat = 0; din_valid = 1'b0; din_data = '0; @(posedge clk); @@ -86,7 +89,9 @@ module tb_idma_otf_transpose #( din_valid = 1'b0; endtask - task automatic capture_outputs(input int unsigned m, n, eb, ne, yt, nt); + task automatic capture_outputs(input int unsigned m, input int unsigned n, + input int unsigned eb, input int unsigned ne, + input int unsigned yt, input int unsigned nt); int unsigned beat = 0; dout_ready = 1'b0; for (int unsigned ct = 0; ct < nt; ct++) @@ -101,13 +106,17 @@ module tb_idma_otf_transpose #( automatic int unsigned tc = rt*ne + e; // transposed col (= original row, 0..m-1) if (tr >= n || tc >= m) begin errors++; - if (errors <= 16) $display("STRB-ON-PAD beat(ct%0d rt%0d k%0d) elem %0d -> (%0d,%0d) OOB", ct, rt, k, e, tr, tc); + if (errors <= 16) + $display("STRB-ON-PAD beat(ct%0d rt%0d k%0d) elem %0d -> (%0d,%0d) OOB", + ct, rt, k, e, tr, tc); end else begin for (int unsigned b = 0; b < eb; b++) begin automatic int gold = gm_get((tr*m + tc)*eb + b); if (int'(dout_data[e*eb + b]) !== gold) begin errors++; - if (errors <= 16) $display("MISMATCH T(%0d,%0d).b%0d=%0d golden=%0d", tr, tc, b, dout_data[e*eb+b], gold); + if (errors <= 16) + $display("MISMATCH T(%0d,%0d).b%0d=%0d golden=%0d", + tr, tc, b, dout_data[e*eb+b], gold); end end wrote[tr*m + tc] = 1'b1; @@ -121,7 +130,8 @@ module tb_idma_otf_transpose #( endtask // Run one m x n transpose of eb-byte elements; returns the mismatch count. - task automatic run_case(input int unsigned m, n, eb, output int unsigned errs); + task automatic run_case(input int unsigned m, input int unsigned n, + input int unsigned eb, output int unsigned errs); automatic int unsigned mode = (eb == 4) ? 2 : (eb == 2) ? 1 : 0; automatic int unsigned ne = StrbWidth / eb; automatic int unsigned yt = (m + ne - 1) / ne; @@ -141,7 +151,8 @@ module tb_idma_otf_transpose #( // clear the engine between cases (resets banks / walkers) clear = 1'b1; repeat (2) @(posedge clk); clear = 1'b0; @(posedge clk); - $display("[TB] case M=%0d N=%0d EB=%0d (tile=%0d elems, %0dx%0d tiles) BP=%0d", m, n, eb, ne, yt, nt, backpressure); + $display("[TB] case M=%0d N=%0d EB=%0d (tile=%0d elems, %0dx%0d tiles) BP=%0d", + m, n, eb, ne, yt, nt, backpressure); fork drive_inputs(m, n, eb, ne, yt, nt); capture_outputs(m, n, eb, ne, yt, nt); join for (int unsigned tr = 0; tr < n; tr++) @@ -155,8 +166,9 @@ module tb_idma_otf_transpose #( initial begin automatic int unsigned total = 0, ce; + automatic int unsigned bp_arg = 0; din_valid = 1'b0; dout_ready = 1'b0; mode_q = '0; m_q = '0; n_q = '0; - if ($test$plusargs("BP")) backpressure = 1'b1; + if ($value$plusargs("BP=%d", bp_arg)) backpressure = (bp_arg != 0); rst_n = 1'b0; clear = 1'b1; repeat (4) @(posedge clk); @@ -168,12 +180,15 @@ module tb_idma_otf_transpose #( for (int unsigned k = 0; k < NCases; k++) begin if (Cases[k][2] > StrbWidth) continue; // element must fit the bus run_case(Cases[k][0], Cases[k][1], Cases[k][2], ce); - if (ce == 0) $display("[TB] PASS: %0dx%0d EB=%0d transpose matches DPI golden", Cases[k][0], Cases[k][1], Cases[k][2]); - else $display("[TB] FAIL: %0dx%0d EB=%0d (%0d errors)", Cases[k][0], Cases[k][1], Cases[k][2], ce); + if (ce == 0) $display("[TB] PASS: %0dx%0d EB=%0d transpose matches DPI golden", + Cases[k][0], Cases[k][1], Cases[k][2]); + else $display("[TB] FAIL: %0dx%0d EB=%0d (%0d errors)", + Cases[k][0], Cases[k][1], Cases[k][2], ce); total += ce; end - if (total == 0) $display("[TB] ALL PASS (%0d cases, StrbWidth=%0d, FullDuplex=%0d)", NCases, StrbWidth, FullDuplex); + if (total == 0) $display("[TB] ALL PASS (%0d cases, StrbWidth=%0d, FullDuplex=%0d)", + NCases, StrbWidth, FullDuplex); else $fatal(1, "[TB] FAIL: %0d total errors", total); repeat (5) @(posedge clk); $finish; diff --git a/test/tb_idma_transpose_b2b.sv b/test/tb_idma_transpose_b2b.sv index 219c20d2..572d34be 100644 --- a/test/tb_idma_transpose_b2b.sv +++ b/test/tb_idma_transpose_b2b.sv @@ -111,7 +111,8 @@ module tb_idma_transpose_b2b .EnableCompute(1'b1), .ComputeOps(idma_pkg::compute_enable_t'{transpose: 1'b1, default: '0}), .ComputeTuning('1), .RAWCouplingAvail(1'b1), .HardwareLegalizer(1'b1), .RejectZeroTransfers(1'b1), - .ErrorCap(idma_pkg::NO_ERROR_HANDLING), .PrintFifoInfo(1'b0), .NumAxInFlight(StrbWidth), .MemSysDepth(0), + .ErrorCap(idma_pkg::NO_ERROR_HANDLING), .PrintFifoInfo(1'b0), + .NumAxInFlight(StrbWidth), .MemSysDepth(0), .idma_req_t(idma_req_t), .idma_rsp_t(idma_rsp_t), .idma_eh_req_t(idma_eh_req_t), .idma_busy_t(idma_busy_t), .axi_req_t(axi_req_t), .axi_rsp_t(axi_rsp_t), .write_meta_channel_t(write_meta_channel_t), .read_meta_channel_t(read_meta_channel_t) @@ -124,8 +125,10 @@ module tb_idma_transpose_b2b .axi_write_req_o(axi_write_req), .axi_write_rsp_i(axi_write_rsp), .busy_o(busy) ); - stream_watchdog #(.NumCycles(4000)) i_r_wd (.clk_i(clk), .rst_ni(rst_n), .valid_i(axi_rsp.r_valid), .ready_i(axi_req.r_ready)); - stream_watchdog #(.NumCycles(4000)) i_w_wd (.clk_i(clk), .rst_ni(rst_n), .valid_i(axi_req.w_valid), .ready_i(axi_rsp.w_ready)); + stream_watchdog #(.NumCycles(4000)) i_r_wd ( + .clk_i(clk), .rst_ni(rst_n), .valid_i(axi_rsp.r_valid), .ready_i(axi_req.r_ready)); + stream_watchdog #(.NumCycles(4000)) i_w_wd ( + .clk_i(clk), .rst_ni(rst_n), .valid_i(axi_req.w_valid), .ready_i(axi_rsp.w_ready)); addr_t sb = 'h0000_1000; @@ -164,9 +167,15 @@ module tb_idma_transpose_b2b nd_req.burst_req.opt.compute.params.transpose.tensor_m = 12'(m); nd_req.burst_req.opt.compute.params.transpose.tensor_n = 12'(n); nd_req.burst_req.opt.last = 1'b1; - nd_req.d_req[0].reps = reps_t'(ne); nd_req.d_req[0].src_strides = addr_t'(int'(n*eb)); nd_req.d_req[0].dst_strides = addr_t'(int'(mp*eb)); - nd_req.d_req[1].reps = reps_t'(yt); nd_req.d_req[1].src_strides = addr_t'(int'(n*eb)); nd_req.d_req[1].dst_strides = addr_t'(int'(ne*eb) - int'((ne-1)*mp*eb)); - nd_req.d_req[2].reps = reps_t'(nt); nd_req.d_req[2].src_strides = addr_t'(int'(ne*eb) - int'((yt*ne-1)*n*eb)); nd_req.d_req[2].dst_strides = addr_t'(int'(mp*eb) - int'((yt-1)*ne*eb)); + nd_req.d_req[0].reps = reps_t'(ne); + nd_req.d_req[0].src_strides = addr_t'(int'(n*eb)); + nd_req.d_req[0].dst_strides = addr_t'(int'(mp*eb)); + nd_req.d_req[1].reps = reps_t'(yt); + nd_req.d_req[1].src_strides = addr_t'(int'(n*eb)); + nd_req.d_req[1].dst_strides = addr_t'(int'(ne*eb) - int'((ne-1)*mp*eb)); + nd_req.d_req[2].reps = reps_t'(nt); + nd_req.d_req[2].src_strides = addr_t'(int'(ne*eb) - int'((yt*ne-1)*n*eb)); + nd_req.d_req[2].dst_strides = addr_t'(int'(mp*eb) - int'((yt-1)*ne*eb)); nd_req_valid = 1'b1; do @(posedge clk); while (!nd_req_ready); // drop valid the cycle accept is seen (compliant) nd_req_valid = 1'b0; @@ -178,14 +187,18 @@ module tb_idma_transpose_b2b for (int unsigned r = 0; r < m; r++) for (int unsigned b = 0; b < eb; b++) if (rd_mem(db + (c*mp + r)*eb + b) !== rd_mem(sb + (r*n + c)*eb + b)) begin - errs++; if (errs <= 8) $display("[B2BT] @db=%0h MISMATCH out_T[%0d][%0d].b%0d", db, c, r, b); + errs++; + if (errs <= 8) + $display("[B2BT] @db=%0h MISMATCH out_T[%0d][%0d].b%0d", db, c, r, b); end for (int unsigned i = 0; i < nt*ne; i++) for (int unsigned j = 0; j < mp; j++) if (i >= n || j >= m) for (int unsigned b = 0; b < eb; b++) if (rd_mem(db + (i*mp + j)*eb + b) !== 8'hCC) begin - errs++; if (errs <= 8) $display("[B2BT] @db=%0h PADDING CLOBBERED row=%0d col=%0d", db, i, j); + errs++; + if (errs <= 8) + $display("[B2BT] @db=%0h PADDING CLOBBERED row=%0d col=%0d", db, i, j); end endtask @@ -209,8 +222,10 @@ module tb_idma_transpose_b2b $display("[B2BT] %0dx%0d EB=%0d: xfer1 -> db=%0h, xfer2 -> db=%0h", m, n, eb, db1, db2); do_transpose(m, n, eb, db1, e1); do_transpose(m, n, eb, db2, e2); // back-to-back, distinct base - if (e1 == 0 && e2 == 0) $display("[B2BT] PASS: %0dx%0d EB=%0d both back-to-back transposes correct", m, n, eb); - else $display("[B2BT] FAIL: %0dx%0d EB=%0d xfer1=%0d xfer2=%0d", m, n, eb, e1, e2); + if (e1 == 0 && e2 == 0) + $display("[B2BT] PASS: %0dx%0d EB=%0d both back-to-back transposes correct", m, n, eb); + else + $display("[B2BT] FAIL: %0dx%0d EB=%0d xfer1=%0d xfer2=%0d", m, n, eb, e1, e2); total += e1 + e2; end diff --git a/test/tb_idma_transpose_nd.sv b/test/tb_idma_transpose_nd.sv index 98eae889..27134622 100644 --- a/test/tb_idma_transpose_nd.sv +++ b/test/tb_idma_transpose_nd.sv @@ -34,8 +34,7 @@ module tb_idma_transpose_nd localparam int unsigned NumDim = 4; // 1D + {row, row-tile, col-tile} localparam logic [NumDim-1:0][31:0] RepWidths = '{default: 32'd16}; - // Geometry cases (M, N, EB) swept in one elaboration: aligned + edge - // (M or N not a multiple of NE) for int8/fp16/fp32. EB>StrbWidth cases skip. + // Geometry cases swept in one elaboration: aligned and edge, int8 and fp16 localparam int unsigned NCases = 13; localparam int unsigned Cases [NCases][3] = '{ '{ 8, 8, 1}, '{16, 16, 1}, '{16, 8, 1}, '{ 8, 8, 2}, '{ 6, 8, 1}, @@ -124,11 +123,13 @@ module tb_idma_transpose_nd // ── Backend (rw_axi) with transpose engine ── idma_backend_rw_axi #( .CombinedShifter(1'b0), .DataWidth(DataWidth), .AddrWidth(AddrWidth), .AxiIdWidth(AxiIdWidth), - .UserWidth(UserWidth), .TFLenWidth(TFLenWidth), .MaskInvalidData(1'b1), .BufferDepth(BufferDepth), + .UserWidth(UserWidth), .TFLenWidth(TFLenWidth), .MaskInvalidData(1'b1), + .BufferDepth(BufferDepth), .EnableCompute(1'b1), .ComputeOps(idma_pkg::compute_enable_t'{transpose: 1'b1, default: '0}), .ComputeTuning('1), .RAWCouplingAvail(1'b1), .HardwareLegalizer(1'b1), .RejectZeroTransfers(1'b1), - .ErrorCap(idma_pkg::NO_ERROR_HANDLING), .PrintFifoInfo(1'b0), .NumAxInFlight(AxIF), .MemSysDepth(0), + .ErrorCap(idma_pkg::NO_ERROR_HANDLING), .PrintFifoInfo(1'b0), + .NumAxInFlight(AxIF), .MemSysDepth(0), .idma_req_t(idma_req_t), .idma_rsp_t(idma_rsp_t), .idma_eh_req_t(idma_eh_req_t), .idma_busy_t(idma_busy_t), .axi_req_t(axi_req_t), .axi_rsp_t(axi_rsp_t), .write_meta_channel_t(write_meta_channel_t), .read_meta_channel_t(read_meta_channel_t) @@ -142,22 +143,24 @@ module tb_idma_transpose_nd ); // watchdogs to surface deadlocks rather than hang forever - stream_watchdog #(.NumCycles(2000)) i_r_wd (.clk_i(clk), .rst_ni(rst_n), .valid_i(axi_rsp.r_valid), .ready_i(axi_req.r_ready)); - stream_watchdog #(.NumCycles(2000)) i_w_wd (.clk_i(clk), .rst_ni(rst_n), .valid_i(axi_req.w_valid), .ready_i(axi_rsp.w_ready)); + stream_watchdog #(.NumCycles(2000)) i_r_wd ( + .clk_i(clk), .rst_ni(rst_n), .valid_i(axi_rsp.r_valid), .ready_i(axi_req.r_ready)); + stream_watchdog #(.NumCycles(2000)) i_w_wd ( + .clk_i(clk), .rst_ni(rst_n), .valid_i(axi_req.w_valid), .ready_i(axi_rsp.w_ready)); // ── Stimulus + check via sim-memory backdoor ── addr_t sb = 'h0000_1000; addr_t db = 'h0000_4000; - // every AW (incl. wstrb=0 padding rows) must stay in the active case's padded - // dst allocation [chk_db, chk_aw_hi) — else a strict slave would DECERR + // every AW must stay inside the active case padded destination logic chk_active = 1'b0; addr_t chk_db, chk_aw_hi; - always @(posedge clk) if (rst_n && chk_active && axi_write_req.aw_valid && axi_write_rsp.aw_ready) begin - if (axi_write_req.aw.addr < chk_db || axi_write_req.aw.addr >= chk_aw_hi) - $fatal(1, "[TPN] AW 0x%0h outside dst alloc [0x%0h,0x%0h) — would DECERR on a strict slave", - axi_write_req.aw.addr, chk_db, chk_aw_hi); - end + always @(posedge clk) + if (rst_n && chk_active && axi_write_req.aw_valid && axi_write_rsp.aw_ready) begin + if (axi_write_req.aw.addr < chk_db || axi_write_req.aw.addr >= chk_aw_hi) + $fatal(1, "[TPN] AW 0x%0h outside dst alloc [0x%0h,0x%0h) — would DECERR on a strict slave", + axi_write_req.aw.addr, chk_db, chk_aw_hi); + end task automatic wr_mem(input addr_t a, input logic [7:0] d); i_axi_sim_mem.mem[a] = d; endtask function automatic logic [7:0] rd_mem(input addr_t a); @@ -210,9 +213,7 @@ module tb_idma_transpose_nd nd_req.burst_req.opt.compute.params.transpose.tensor_m = 12'(m); nd_req.burst_req.opt.compute.params.transpose.tensor_n = 12'(n); nd_req.burst_req.opt.last = 1'b1; - // ND midend strides are INCREMENTAL deltas (added on dim roll-over), NOT - // absolute pitches. Aᵀ uses padded pitch mp*eb (aligned writes); src keeps - // n*eb (misaligned reads coalesce in the pre-engine buffer). + // ND midend strides are incremental deltas, not absolute pitches nd_req.d_req[0].reps = reps_t'(ne); nd_req.d_req[0].src_strides = addr_t'(int'(n*eb)); nd_req.d_req[0].dst_strides = addr_t'(int'(mp*eb)); @@ -243,7 +244,8 @@ module tb_idma_transpose_nd automatic logic [7:0] exp = rd_mem(sb + (r*n + c)*eb + b); if (got !== exp) begin errs++; - if (errs <= 12) $display("[TPN] MISMATCH out_T[%0d][%0d].b%0d=%02h exp %02h", c, r, b, got, exp); + if (errs <= 12) + $display("[TPN] MISMATCH out_T[%0d][%0d].b%0d=%02h exp %02h", c, r, b, got, exp); end end // check 2: padding cols [m,mp) and padding rows [n,nt*ne) must stay sentinel @@ -254,7 +256,9 @@ module tb_idma_transpose_nd automatic logic [7:0] got = rd_mem(db + (i*mp + j)*eb + b); if (got !== 8'hCC) begin errs++; - if (errs <= 12) $display("[TPN] PADDING CLOBBERED at row=%0d col=%0d b%0d=%02h (exp CC)", i, j, b, got); + if (errs <= 12) + $display("[TPN] PADDING CLOBBERED at row=%0d col=%0d b%0d=%02h (exp CC)", + i, j, b, got); end end endtask @@ -270,7 +274,8 @@ module tb_idma_transpose_nd if (Cases[k][2] > StrbWidth) continue; // element must fit the bus run_case(Cases[k][0], Cases[k][1], Cases[k][2], ce); if (ce == 0) $display("[TPN] PASS: %0dx%0d EB=%0d", Cases[k][0], Cases[k][1], Cases[k][2]); - else $display("[TPN] FAIL: %0dx%0d EB=%0d (%0d mismatches)", Cases[k][0], Cases[k][1], Cases[k][2], ce); + else $display("[TPN] FAIL: %0dx%0d EB=%0d (%0d mismatches)", + Cases[k][0], Cases[k][1], Cases[k][2], ce); total += ce; end diff --git a/test/tpl/tb_idma_backend.sv.tpl b/test/tpl/tb_idma_backend.sv.tpl index 296911bd..29e0e7fd 100644 --- a/test/tpl/tb_idma_backend.sv.tpl +++ b/test/tpl/tb_idma_backend.sv.tpl @@ -27,8 +27,7 @@ module tb_idma_backend_${name_uniqueifier} import idma_pkg::*; #( %endif parameter int unsigned AddrWidth = 32, parameter int unsigned UserWidth = 1, - // ID is currently used to differentiate transfers in testbench. We need to fix this - // eventually. + // ID is currently used to differentiate transfers in testbench. We need to fix this eventually. parameter int unsigned AxiIdWidth = \ % if 'tilelink' in used_protocols: 12, @@ -72,8 +71,7 @@ module tb_idma_backend_${name_uniqueifier} import idma_pkg::*; #( localparam bit ModelOutput = 1'b0; localparam bit PrintFifoInfo = 1'b1; - // TB parameters - // dependent parameters + // TB parameters dependent parameters localparam int unsigned StrbWidth = DataWidth / 8; localparam int unsigned OffsetWidth = $clog2(StrbWidth); @@ -694,7 +692,7 @@ ${p}_${database[p]['write_meta_channel']}_width\ // DMA Tracer //-------------------------------------- // only activate tracer if requested - if (DmaTracing) begin + if (DmaTracing) begin : gen_dma_tracer // fetch the name of the trace file from CMD line string trace_file; initial begin @@ -1094,8 +1092,6 @@ axi_rsp_mem ) else writes_in_flight[proto][id] = 1; - //if (writes_in_flight[proto][id] == 1) - //$display("Started transfer %d id @%d ns", id, $time); end if ( axi_rsp_mem.b_valid && axi_req_mem.b_ready ) begin id = axi_rsp_mem.b.id; @@ -1106,8 +1102,6 @@ axi_rsp_mem ) if ( writes_in_flight[proto][id] == 0 ) $fatal(1, "Tried to decrement 0"); writes_in_flight[proto][id]--; - //if (writes_in_flight[proto][id] == 0) - //$display("Stopped transfer %d id @%d ns", id, $time); end % elif protocol == 'obi': // obi_sim_mem commits writes on grant; mem current at rsp_valid, no write tracking @@ -1127,8 +1121,6 @@ axi_rsp_mem ) else writes_in_flight[proto][id] = 1; - //if (writes_in_flight[proto][id] == 1) - //$display("Started transfer %d id @%d ns", id, $time); end if ( ${protocol}_axi_rsp_mem.b_valid && ${protocol}_axi_req_mem.b_ready ) begin % if protocol == 'axi_lite': @@ -1145,8 +1137,6 @@ axi_rsp_mem ) if ( writes_in_flight[proto][id] == 0 ) $fatal(1, "Tried to decrement 0"); writes_in_flight[proto][id]--; - //if (writes_in_flight[proto][id] == 0) - //$display("Stopped transfer %d id @%d ns", id, $time); end % endif % endfor @@ -1209,8 +1199,6 @@ axi_rsp_mem ) @(posedge clk); end end - // finished job - // $display("vvv Finished: vvv%s\n^^^ Finished: ^^^", now.pprint()); // launch model model.transfer( now.length,