diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7e03f09..e5c872b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,7 +19,7 @@ env: jobs: check-clean: - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 @@ -40,7 +40,7 @@ jobs: make clean git status && test -z "$(git status --porcelain)" check-stale: - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 @@ -60,7 +60,7 @@ jobs: make -B pickle doc graph regs git status && test -z "$(git status --porcelain)" lint: - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 needs: [check-clean, check-stale] strategy: fail-fast: false @@ -83,7 +83,7 @@ jobs: if: ${{ matrix.lint_check == 'python' }} run: scripts/python-lint lint-commit: - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 needs: [check-clean, check-stale] steps: - uses: actions/checkout@v4 @@ -117,7 +117,7 @@ jobs: # reviewdog_reporter: github-check analyze-contributors: - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 needs: lint steps: - uses: actions/checkout@v4 @@ -130,7 +130,7 @@ jobs: path: contributions.txt retention-days: 7 analyze-todos: - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 needs: lint continue-on-error: true steps: diff --git a/.gitignore b/.gitignore index 7b98fe0..3ff8e0f 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,8 @@ pickle doc/morty* bender morty* + +ucli.key +vc_hdrs.h +work-vcs/ +lmgrd.daidir/ diff --git a/Bender.lock b/Bender.lock index b90e0e9..ab421cb 100644 --- a/Bender.lock +++ b/Bender.lock @@ -7,8 +7,8 @@ packages: dependencies: - common_cells axi: - revision: 587355b77b8ce94dcd600efbd5d5bd118ff913a7 - version: 0.39.4 + revision: a8c53cee2520d328d390c507c88818e8ac144c8c + version: 0.39.10 source: Git: https://github.com/pulp-platform/axi.git dependencies: @@ -16,22 +16,22 @@ packages: - common_verification - tech_cells_generic common_cells: - revision: c27bce39ebb2e6bae52f60960814a2afca7bd4cb - version: 1.37.0 + revision: 9ca8a7655f741e7dd5736669a20a301325194c28 + version: 1.39.0 source: Git: https://github.com/pulp-platform/common_cells.git dependencies: - common_verification - tech_cells_generic common_verification: - revision: 9c07fa860593b2caabd9b5681740c25fac04b878 - version: 0.2.3 + revision: fb1885f48ea46164a10568aeff51884389f67ae3 + version: 0.2.5 source: Git: https://github.com/pulp-platform/common_verification.git dependencies: [] register_interface: - revision: ae616e5a1ec2b41e72d200e5ab09c65e94aebd3d - version: 0.4.4 + revision: d6e1d4cdaab7870f4faf3f88a1c788eaf5ac129d + version: 0.4.7 source: Git: https://github.com/pulp-platform/register_interface.git dependencies: diff --git a/Bender.yml b/Bender.yml index 5402fa3..06fa41c 100644 --- a/Bender.yml +++ b/Bender.yml @@ -9,7 +9,7 @@ package: - "Wolfgang Roenninger " dependencies: - axi: { git: "https://github.com/pulp-platform/axi.git", version: 0.39.4 } + axi: { git: "https://github.com/pulp-platform/axi.git", version: 0.39.10 } common_cells: { git: "https://github.com/pulp-platform/common_cells.git", version: 1.21.0 } common_verification: { git: "https://github.com/pulp-platform/common_verification.git", version: 0.2.0 } register_interface: { git: "https://github.com/pulp-platform/register_interface.git", version: 0.4.4 } diff --git a/Makefile b/Makefile index fee2132..482fae1 100644 --- a/Makefile +++ b/Makefile @@ -6,6 +6,7 @@ BENDER ?= bender PYTHON ?= python3 +VSIM ?= vsim REGGEN_PATH = $(shell $(BENDER) path register_interface)/vendor/lowrisc_opentitan/util/regtool.py REGGEN = $(PYTHON) $(REGGEN_PATH) @@ -30,6 +31,7 @@ help: @echo "-------------" @echo "" @echo "bin/axi_llc.vcs: creates the VCS executable" + @echo "sim_id_widths: simulates representative AXI ID lookup widths" @echo "pickle: uses morty to generate a pickled version of the hardware" @echo "doc: generates the documentation in doc/morty" @echo "graph: generates the module hierarchy graph in doc/morty-graph" @@ -52,7 +54,7 @@ regs: # QuestaSim # -------------- -.PHONY: sim_clean +.PHONY: sim_id_widths sim_clean VLOG_ARGS += -suppress vlog-2583 -suppress vlog-13314 -suppress vlog-13233 -timescale \"1 ns / 1 ps\" @@ -65,6 +67,9 @@ endef scripts/compile_vsim.tcl: Bender.yml $(call generate_vsim, $@, -t rtl -t test,..) +sim_id_widths: scripts/compile_vsim.tcl + $(VSIM) -c -do "source scripts/compile_vsim.tcl; source scripts/test_id_widths_vsim.tcl" + sim_clean: rm -rf scripts/compile_vsim.tcl rm -rf work diff --git a/scripts/start_vcs b/scripts/start_vcs index 976ad0b..58fb54c 100644 --- a/scripts/start_vcs +++ b/scripts/start_vcs @@ -3,4 +3,4 @@ # Solderpad Hardware License, Version 0.51, see LICENSE for details. # SPDX-License-Identifier: SHL-0.51 -bin/axi_llc.vcs +vcs+lic+wait | tee logs/axi_llc.vcs.log +bin/axi_llc.vcs +vcs+lic+wait +ntb_random_seed=1 | tee logs/axi_llc.vcs.log diff --git a/scripts/start_vsim.tcl b/scripts/start_vsim.tcl index de911e6..0f897ed 100644 --- a/scripts/start_vsim.tcl +++ b/scripts/start_vsim.tcl @@ -4,7 +4,7 @@ # Author: Thomas Benz -vsim -t 1ps -voptargs=+acc tb_axi_llc -logfile logs/axi_llc.vsim.log -wlf logs/axi_llc.wlf +vsim -t 1ps -voptargs=+acc -sv_seed 1 tb_axi_llc -logfile logs/axi_llc.vsim.log -wlf logs/axi_llc.wlf set StdArithNoWarnings 1 set NumericStdNoWarnings 1 diff --git a/scripts/test_id_widths_vsim.tcl b/scripts/test_id_widths_vsim.tcl new file mode 100644 index 0000000..03e7430 --- /dev/null +++ b/scripts/test_id_widths_vsim.tcl @@ -0,0 +1,50 @@ +# Copyright 2026 ETH Zurich and University of Bologna. +# Solderpad Hardware License, Version 0.51, see LICENSE for details. +# SPDX-License-Identifier: SHL-0.51 +# +# Author: Philippe Sauter + +set regression_failed 0 + +proc run_id_width_test {name id_width lookup_bits} { + global regression_failed + + puts "================================================================" + puts "= Running AXI ID configuration: width=$id_width lookup=$lookup_bits" + puts "================================================================" + + set transcript_name "logs/axi_llc.id_width_${name}.vsim.log" + file delete -force $transcript_name + transcript file $transcript_name + + vsim -t 1ps -voptargs=+acc -sv_seed 1 \ + -GTbAxiIdWidthFull=$id_width \ + -GTbAxiIdLookupBits=$lookup_bits \ + -wlf "logs/axi_llc.id_width_${name}.wlf" \ + tb_axi_llc + + onfinish stop + set StdArithNoWarnings 1 + set NumericStdNoWarnings 1 + run 100 us + quit -sim + + transcript file {} + set transcript_fd [open $transcript_name r] + set transcript_data [read $transcript_fd] + close $transcript_fd + if {[regexp -line {^# \*\* (Error|Fatal)( \([^)]*\))?:} $transcript_data]} { + puts "Regression errors detected in $transcript_name" + set regression_failed 1 + } +} + +run_id_width_test width1_lookup1 1 1 +run_id_width_test width2_lookup1 2 1 +run_id_width_test width3_lookup3 3 3 +run_id_width_test width5_lookup3 5 3 +run_id_width_test width6_lookup2 6 2 +run_id_width_test width6_lookup4 6 4 +run_id_width_test width6_lookup5 6 5 + +quit -code $regression_failed -f diff --git a/src/axi_llc_config.sv b/src/axi_llc_config.sv index 8f4f59c..b9d9349 100644 --- a/src/axi_llc_config.sv +++ b/src/axi_llc_config.sv @@ -448,16 +448,16 @@ module axi_llc_config #( // this state determines which cache way should be flushed // it also sets up the counters for state-keeping how far // the flush operation has progressed - // define if the user requested a flush to_flush_d = (conf_regs_i.cfg_flush | conf_regs_i.cfg_spm) & ~conf_regs_i.flushed; - conf_regs_o.flushed = conf_regs_i.cfg_spm & conf_regs_i.flushed; - conf_regs_o.flushed_en = 1'b1; // now determine if we have something to do at all if (to_flush_d == '0) begin // nothing to flush, go to idle flush_state_d = FsmIdle; // clear the cfg_flush register. conf_regs_o.cfg_flush = set_asso_t'(1'b0); + // reset the flushed register to SPM. + conf_regs_o.flushed = conf_regs_i.cfg_spm; + conf_regs_o.flushed_en = 1'b1; end else begin flush_state_d = FsmSendFlush; load_cnt = 1'b1; diff --git a/src/axi_llc_hit_miss.sv b/src/axi_llc_hit_miss.sv index b5ce9c1..b8f2ff8 100644 --- a/src/axi_llc_hit_miss.sv +++ b/src/axi_llc_hit_miss.sv @@ -48,7 +48,9 @@ module axi_llc_hit_miss #( /// Way indicator, is a onehot signal with width: `Cfg.SetAssociativity`. parameter type way_ind_t = logic, /// Whether to print SRAM configs - parameter bit PrintSramCfg = 0 + parameter bit PrintSramCfg = 0, + /// Number of low AXI ID bits used for miss counters. + parameter int unsigned AxiIdLookupBits = 32'd0 ) ( /// Clock, positive edge triggered. input logic clk_i, @@ -360,8 +362,9 @@ module axi_llc_hit_miss #( assign cnt_up.valid = ~desc_o.flush & miss_valid_o & miss_ready_i; axi_llc_miss_counters #( - .Cfg ( Cfg ), - .cnt_t ( cnt_t ) + .Cfg ( Cfg ), + .AxiIdLookupBits ( AxiIdLookupBits ), + .cnt_t ( cnt_t ) ) i_miss_counters ( .clk_i ( clk_i ), .rst_ni ( rst_ni ), diff --git a/src/axi_llc_pkg.sv b/src/axi_llc_pkg.sv index 7e5c096..9e3da56 100644 --- a/src/axi_llc_pkg.sv +++ b/src/axi_llc_pkg.sv @@ -229,11 +229,6 @@ package axi_llc_pkg; parameter int unsigned MissCntWidth = 32'd5; /// Writes are counted separately. Writes have to be in order, only one counter. parameter int unsigned MissCntMaxWWidth = 32'd7; - /// This number tells us how many bits of the slave port AXI ID are used for pointing on a counter - /// * Translates in 2**`UseIdBits` counters inferred. - /// * Set this parameter to the slave port AXI ID width if you want one counter for each AXI ID. - parameter int unsigned UseIdBits = 32'd4; - /// This adds a spill register in the response path of the tag stroage unit. /// This should be used to achieve good timing characteristics in synthsis as the longest /// path in the design comes out of the tag storage macros. diff --git a/src/axi_llc_reg_wrap.sv b/src/axi_llc_reg_wrap.sv index f5de663..46accc8 100644 --- a/src/axi_llc_reg_wrap.sv +++ b/src/axi_llc_reg_wrap.sv @@ -189,7 +189,11 @@ module axi_llc_reg_wrap #( parameter type axi_addr_t = logic[AxiAddrWidth-1:0], /// Dependent parameter, do **not** overwrite! /// Data type of set associativity wide registers - parameter type way_ind_t = logic[SetAssociativity-1:0] + parameter type way_ind_t = logic[SetAssociativity-1:0], + /// Number of low AXI ID bits used for demux tracking and miss counters. + /// The number of inferred bookkeeping entries grows exponentially with this value. + parameter int unsigned AxiIdLookupBits = + (AxiIdWidth < 32'd4) ? AxiIdWidth : 32'd4 ) ( /// Rising-edge clock of all ports. input logic clk_i, @@ -264,6 +268,7 @@ module axi_llc_reg_wrap #( .NumLines ( NumLines ), .NumBlocks ( NumBlocks ), .AxiIdWidth ( AxiIdWidth ), + .AxiIdLookupBits ( AxiIdLookupBits ), .AxiAddrWidth ( AxiAddrWidth ), .AxiDataWidth ( AxiDataWidth ), .AxiUserWidth ( AxiUserWidth ), diff --git a/src/axi_llc_top.sv b/src/axi_llc_top.sv index 443b5ed..4171eaa 100644 --- a/src/axi_llc_top.sv +++ b/src/axi_llc_top.sv @@ -5,6 +5,8 @@ // Author: Wolfgang Roenninger // Date: 30.04.2019 +`include "common_cells/assertions.svh" + /// Contains the top_level of the axi_llc with structs as AXI connections. /// The standard configuration is a cache size of 512KByte with a set-associativity /// of 8, and line length of 8 blocks, one block equals the AXI data width of the @@ -187,7 +189,11 @@ module axi_llc_top #( parameter type axi_addr_t = logic[AxiAddrWidth-1:0], /// Dependent parameter, do **not** overwrite! /// Data type of set associativity wide registers - parameter type way_ind_t = logic[SetAssociativity-1:0] + parameter type way_ind_t = logic[SetAssociativity-1:0], + /// Number of low AXI ID bits used for demux tracking and miss counters. + /// The number of inferred bookkeeping entries grows exponentially with this value. + parameter int unsigned AxiIdLookupBits = + (AxiIdWidth < 32'd4) ? AxiIdWidth : 32'd4 ) ( /// Rising-edge clock of all ports. input logic clk_i, @@ -470,7 +476,7 @@ module axi_llc_top #( .axi_resp_t ( slv_resp_t ), .NoMstPorts ( 32'd2 ), .MaxTrans ( axi_llc_pkg::MaxTrans ), - .AxiLookBits ( axi_llc_pkg::UseIdBits ), + .AxiLookBits ( AxiIdLookupBits ), .SpillAw ( 1'b0 ), .SpillW ( 1'b0 ), .SpillB ( 1'b0 ), @@ -567,13 +573,14 @@ module axi_llc_top #( ); axi_llc_hit_miss #( - .Cfg ( Cfg ), - .AxiCfg ( AxiCfg ), - .desc_t ( llc_desc_t ), - .lock_t ( lock_t ), - .cnt_t ( cnt_t ), - .way_ind_t ( way_ind_t ), - .PrintSramCfg ( PrintSramCfg ) + .Cfg ( Cfg ), + .AxiCfg ( AxiCfg ), + .AxiIdLookupBits ( AxiIdLookupBits ), + .desc_t ( llc_desc_t ), + .lock_t ( lock_t ), + .cnt_t ( cnt_t ), + .way_ind_t ( way_ind_t ), + .PrintSramCfg ( PrintSramCfg ) ) i_hit_miss_unit ( .clk_i, .rst_ni, @@ -933,6 +940,11 @@ module axi_llc_top #( default: '0 }; + `ASSERT_INIT(AxiIdLookupBitsNonZero, AxiIdLookupBits > 32'd0, + "AxiIdLookupBits must be greater than zero.") + `ASSERT_INIT(AxiIdLookupBitsValid, AxiIdLookupBits <= AxiIdWidth, + "AxiIdLookupBits must not exceed AxiIdWidth.") + // pragma translate_off `ifndef VERILATOR initial begin : proc_assert_axi_params @@ -941,7 +953,7 @@ module axi_llc_top #( axi_id_width : assert(AxiIdWidth > 32'd0) else $fatal(1, "Parameter `AxiIdWidth` has to be > 0!"); axi_data_width : assert(AxiDataWidth inside {32'd8, 32'd16, 32'd32, 32'd64, - 32'd128, 32'd256, 32'd512, 32'd1028}) else + 32'd128, 32'd256, 32'd512, 32'd1024}) else $fatal(1, "Parameter `AxiDataWidth` has to be inside the AXI4+ATOP specification!"); axi_user_width : assert(AxiUserWidth > 32'd0) else $fatal(1, "Parameter `AxiUserWidth` has to be > 0!"); diff --git a/src/hit_miss_detect/axi_llc_miss_counters.sv b/src/hit_miss_detect/axi_llc_miss_counters.sv index 262c683..93a457e 100644 --- a/src/hit_miss_detect/axi_llc_miss_counters.sv +++ b/src/hit_miss_detect/axi_llc_miss_counters.sv @@ -19,7 +19,9 @@ module axi_llc_miss_counters #( /// logic rw; // 0:read, 1:write /// logic valid; // valid, equals enable of the counter /// } cnt_t; - parameter type cnt_t = logic + parameter type cnt_t = logic, + /// Number of low AXI ID bits used to select a miss counter. + parameter int unsigned AxiIdLookupBits = 32'd0 ) ( /// Clock, positive edge triggered. input logic clk_i, @@ -34,7 +36,7 @@ module axi_llc_miss_counters #( /// One of the counters is overflowing, stall descriptor! output logic stall_o ); - localparam int unsigned NoCounters = 2**axi_llc_pkg::UseIdBits; + localparam int unsigned NoCounters = 2**AxiIdLookupBits; // stall signal for each counter (no minus 1, because the write counter is extra ) logic [NoCounters:0] stall; logic [NoCounters-1:0] en; @@ -55,12 +57,12 @@ module axi_llc_miss_counters #( en[i] = 1'b0; down[i] = 1'b0; // we should count up - if ((cnt_up_i.id[0+:axi_llc_pkg::UseIdBits] == i) && cnt_up_i.valid) begin + if ((cnt_up_i.id[0+:AxiIdLookupBits] == i) && cnt_up_i.valid) begin en[i] = 1'b1; end // we should count down, or do nothing, if we are already counting up - if ((cnt_down_i.id[0+:axi_llc_pkg::UseIdBits] == i) && cnt_down_i.valid) begin + if ((cnt_down_i.id[0+:AxiIdLookupBits] == i) && cnt_down_i.valid) begin if (en[i] == 1'b1) begin en[i] = 1'b0; end else begin @@ -69,7 +71,7 @@ module axi_llc_miss_counters #( end end // do we have to send the descriptor to the miss pipeline? - if (cnt_up_i.id[0+:axi_llc_pkg::UseIdBits] == i) begin + if (cnt_up_i.id[0+:AxiIdLookupBits] == i) begin // first check the counter mapped to the id to_miss_o = |q_miss[i]; // if it is a write also check if there are other writes in the miss pipeline diff --git a/src/hit_miss_detect/axi_llc_tag_store.sv b/src/hit_miss_detect/axi_llc_tag_store.sv index 04db770..a727d3a 100644 --- a/src/hit_miss_detect/axi_llc_tag_store.sv +++ b/src/hit_miss_detect/axi_llc_tag_store.sv @@ -298,19 +298,20 @@ module axi_llc_tag_store #( ); // comparator (XNOR) - assign ram_compared = tag_data_t'{ - val: bist_pattern.val, - dit: bist_pattern.dit, - tag: (req_q.mode == axi_llc_pkg::Bist) ? bist_pattern.tag : req_q.tag - } ~^ ram_rdata; - assign tag_equ[i] = &ram_compared.tag; // valid if the stored tag equals the one looked up - assign tag_val[i] = ram_rdata.val; // indicates where valid values are in the line - assign tag_dit[i] = ram_rdata.dit; // indicates which tags are dirty + tag_t tag_compare; + logic i_tag_equ; + + assign tag_compare = (req_q.mode == axi_llc_pkg::Bist) ? bist_pattern.tag : req_q.tag; + assign i_tag_equ = &(ram_rdata.tag ~^ tag_compare); + assign tag_equ[i] = i_tag_equ; + assign tag_val[i] = ram_rdata.val; + assign tag_dit[i] = ram_rdata.dit; + + assign hit[i] = req_q.indicator[i] & tag_val[i] & tag_equ[i]; + assign bist_res[i] = (ram_rdata.val == bist_pattern.val) & + (ram_rdata.dit == bist_pattern.dit) & + i_tag_equ; - // hit detection - assign hit[i] = req_q.indicator[i] & tag_val[i] & tag_equ[i]; - // BIST also add the two bits of valid and dirty - assign bist_res[i] = ram_compared.val & ram_compared.dit & tag_equ[i]; // assignment to wide output signal that goes to the tag output mux assign stored_tag[i] = ram_rdata; end diff --git a/test/tb_axi_llc.sv b/test/tb_axi_llc.sv index c1ec292..10c699c 100644 --- a/test/tb_axi_llc.sv +++ b/test/tb_axi_llc.sv @@ -17,6 +17,9 @@ module tb_axi_llc #( parameter int unsigned TbNumBlocks = 32'd8, /// ID width of the Full AXI slave port, master port has ID `AxiIdWidthFull + 32'd1` parameter int unsigned TbAxiIdWidthFull = 32'd6, + /// Number of low AXI ID bits used for demux tracking and miss counters. + parameter int unsigned TbAxiIdLookupBits = + (TbAxiIdWidthFull < 32'd4) ? TbAxiIdWidthFull : 32'd4, /// Address width of the full AXI bus parameter int unsigned TbAxiAddrWidthFull = 32'd32, /// Data width of the full AXI bus @@ -186,7 +189,8 @@ module tb_axi_llc #( conf_req_t reg_cfg_req; conf_rsp_t reg_cfg_rsp; // Tb signals - logic enable_counters, print_counters, enable_progress; + logic enable_counters, print_counters, reset_counters, enable_progress; + logic assert_cache_hits, assert_no_cache_hits; /////////////////////// // AXI DV interfaces // @@ -283,7 +287,10 @@ module tb_axi_llc #( reg_conf_driver.reset_master(); enable_counters = 1'b0; print_counters = 1'b0; + reset_counters = 1'b0; enable_progress = 1'b0; + assert_cache_hits = 1'b0; + assert_no_cache_hits = 1'b0; // Set some mem regions for rand axi master axi_master.add_memory_region(CachedRegionStart, CachedRegionStart + 2*CachedRegionLength, @@ -327,13 +334,17 @@ module tb_axi_llc #( reg_conf_driver.send_read(VersionLow, cfg_data, cfg_error); reg_conf_driver.send_read(VersionHigh, cfg_data, cfg_error); - $info("Random read and write"); + $info("\n\nRandom read and write"); + reset_perf_counters(); axi_master.run(TbNumReads, TbNumWrites); + assert_cache_hits = 1; + print_perf_counters(); + flush_all(reg_conf_driver); compare_mems(cpu_scoreboard, mem_scoreboard); clear_spm_cpu(cpu_scoreboard); - $info("Enable lower half SPM"); + $info("\n\nEnable lower half SPM"); cfg_addr = CfgSpmLow; cfg_data = {((TbSetAssociativity == 32'd1) ? 32'd1 : (TbSetAssociativity/2)){1'b1}}; cfg_wstrb = 4'hF; @@ -342,52 +353,96 @@ module tb_axi_llc #( cfg_data = 32'd1; cfg_wstrb = 4'hF; reg_conf_driver.send_write(cfg_addr, cfg_data, cfg_wstrb, cfg_error); + + reset_perf_counters(); axi_master.run(TbNumReads, TbNumWrites); + // TODO: How to check SPM; when cache-only also indicates SPM hits/misses? + assert_cache_hits = 1; + print_perf_counters(); + flush_all(reg_conf_driver); compare_mems(cpu_scoreboard, mem_scoreboard); clear_spm_cpu(cpu_scoreboard); - $info("All SPM"); + $info("\n\nAll SPM"); cfg_addr = CfgSpmLow; - cfg_data = {32{1'b1}}; + cfg_data = '1; cfg_wstrb = 4'hF; reg_conf_driver.send_write(cfg_addr, cfg_data, cfg_wstrb, cfg_error); cfg_addr = CfgSpmHigh; - cfg_data = {32{1'b1}}; + cfg_data = '1; cfg_wstrb = 4'hF; reg_conf_driver.send_write(cfg_addr, cfg_data, cfg_wstrb, cfg_error); cfg_addr = CommitCfg; cfg_data = 32'd1; cfg_wstrb = 4'hF; reg_conf_driver.send_write(cfg_addr, cfg_data, cfg_wstrb, cfg_error); + + reset_perf_counters(); axi_master.run(TbNumReads, TbNumWrites); + assert_no_cache_hits = 1; + print_perf_counters(); + flush_all(reg_conf_driver); compare_mems(cpu_scoreboard, mem_scoreboard); clear_spm_cpu(cpu_scoreboard); - $info("Random read and write"); + // Note: depends on the previous test being "All SPM" + $info("\n\nCoalesced explicit and SPM flushes"); cfg_addr = CfgSpmLow; - cfg_data = 32'b0; + cfg_data = '0; cfg_wstrb = 4'hF; reg_conf_driver.send_write(cfg_addr, cfg_data, cfg_wstrb, cfg_error); cfg_addr = CfgSpmHigh; - cfg_data = 32'b0; + cfg_data = '0; + cfg_wstrb = 4'hF; + reg_conf_driver.send_write(cfg_addr, cfg_data, cfg_wstrb, cfg_error); + cfg_addr = CfgFlushLow; + cfg_data = '1; + cfg_wstrb = 4'hF; + reg_conf_driver.send_write(cfg_addr, cfg_data, cfg_wstrb, cfg_error); + cfg_addr = CfgFlushHigh; + cfg_data = '1; cfg_wstrb = 4'hF; reg_conf_driver.send_write(cfg_addr, cfg_data, cfg_wstrb, cfg_error); cfg_addr = CommitCfg; cfg_data = 32'd1; cfg_wstrb = 4'hF; reg_conf_driver.send_write(cfg_addr, cfg_data, cfg_wstrb, cfg_error); - axi_master.run(TbNumReads, TbNumWrites); - print_perf_couters(); + reset_perf_counters(); + axi_master.run(TbNumReads, TbNumWrites); + assert_cache_hits = 1; + print_perf_counters(); flush_all(reg_conf_driver); compare_mems(cpu_scoreboard, mem_scoreboard); clear_spm_cpu(cpu_scoreboard); + $info("\n\nRandom read and write"); + cfg_addr = CfgSpmLow; + cfg_data = '0; + cfg_wstrb = 4'hF; + reg_conf_driver.send_write(cfg_addr, cfg_data, cfg_wstrb, cfg_error); + cfg_addr = CfgSpmHigh; + cfg_data = '0; + cfg_wstrb = 4'hF; + reg_conf_driver.send_write(cfg_addr, cfg_data, cfg_wstrb, cfg_error); + cfg_addr = CommitCfg; + cfg_data = 32'd1; + cfg_wstrb = 4'hF; + reg_conf_driver.send_write(cfg_addr, cfg_data, cfg_wstrb, cfg_error); - $display("Tests ended!"); + reset_perf_counters(); + axi_master.run(TbNumReads, TbNumWrites); + assert_cache_hits = 1; + print_perf_counters(); + + flush_all(reg_conf_driver); + compare_mems(cpu_scoreboard, mem_scoreboard); + clear_spm_cpu(cpu_scoreboard); + + $display("\n\nTests ended!"); $finish(); end @@ -440,13 +495,19 @@ module tb_axi_llc #( $info("Finished flushing the cache!"); endtask : flush_all - task print_perf_couters(); + task print_perf_counters(); @(negedge clk); print_counters = 1'b1; @(negedge clk); print_counters = 1'b0; - endtask : print_perf_couters + endtask : print_perf_counters + task reset_perf_counters(); + @(negedge clk); + reset_counters = 1'b1; + @(negedge clk); + reset_counters = 1'b0; + endtask : reset_perf_counters /////////////////////// // Design under test // @@ -456,6 +517,7 @@ module tb_axi_llc #( .NumLines ( TbNumLines ), .NumBlocks ( TbNumBlocks ), .AxiIdWidth ( TbAxiIdWidthFull ), + .AxiIdLookupBits ( TbAxiIdLookupBits ), .AxiAddrWidth ( TbAxiAddrWidthFull ), .AxiDataWidth ( TbAxiDataWidthFull ), .AxiUserWidth ( TbAxiUserWidthFull ), @@ -704,12 +766,46 @@ module tb_axi_llc #( $display("w_chan_unit_req: %f", real'(count[50]) / real'(cycle_count)); $display("r_chan_unit_req: %f", real'(count[51]) / real'(cycle_count)); $display("##################################################################"); - // After printing, reset the counters. + + // Note: these assertions may, in theory, not be hit given random + // test input data. We fix a random seed for the test to prevent this. + if (assert_cache_hits) begin + assert (count[17] != 0) else $error("aw_desc_cache is zero"); + assert (count[19] != 0) else $error("ar_desc_cache is zero"); + assert (count[31] != 0) else $error("hit_write_cache is zero"); + assert (count[33] != 0) else $error("hit_read_cache is zero"); + assert (count[35] != 0) else $error("miss_write_cache is zero"); + assert (count[37] != 0) else $error("miss_read_cache is zero"); + assert (count[39] != 0) else $error("refill_write is zero"); + assert (count[41] != 0) else $error("refill_read is zero"); + assert (count[43] != 0) else $error("evict_write is zero"); + assert (count[45] != 0) else $error("evict_read is zero"); + end + + if (assert_no_cache_hits) begin + assert (count[17] == 0) else $error("aw_desc_cache is non-zero"); + assert (count[19] == 0) else $error("ar_desc_cache is non-zero"); + assert (count[31] == 0) else $error("hit_write_cache is non-zero"); + assert (count[33] == 0) else $error("hit_read_cache is non-zero"); + assert (count[35] == 0) else $error("miss_write_cache is non-zero"); + assert (count[37] == 0) else $error("miss_read_cache is non-zero"); + assert (count[39] == 0) else $error("refill_write is non-zero"); + assert (count[41] == 0) else $error("refill_read is non-zero"); + assert (count[43] == 0) else $error("evict_write is non-zero"); + assert (count[45] == 0) else $error("evict_read is non-zero"); + end + + assert_cache_hits = 0; + assert_no_cache_hits = 0; + end // print counters + + if (reset_counters) begin + // Reset the counters. cycle_count = 0; for (int unsigned i = 0; i < NumCounters; i++) begin count[i] = 0; end - end // print counters + end // reset counters end // forever begin end : proc_counters endmodule