diff --git a/vendor/lowrisc_ip.lock.hjson b/vendor/lowrisc_ip.lock.hjson index 5666f668..600db907 100644 --- a/vendor/lowrisc_ip.lock.hjson +++ b/vendor/lowrisc_ip.lock.hjson @@ -9,6 +9,6 @@ upstream: { url: https://github.com/lowRISC/opentitan - rev: d78da129c7f2b115ccabd1c3af199e0e5812f365 + rev: c91b50f357a76dae2ada104e397f6a91f72a33da } } diff --git a/vendor/lowrisc_ip.vendor.hjson b/vendor/lowrisc_ip.vendor.hjson index 3e3710da..803140f6 100644 --- a/vendor/lowrisc_ip.vendor.hjson +++ b/vendor/lowrisc_ip.vendor.hjson @@ -11,19 +11,20 @@ } mapping: [ - {from: "hw/dv/sv/common_ifs", to: "common_ifs"}, - {from: "hw/dv/sv/csr_utils", to: "csr_utils"}, - {from: "hw/dv/sv/dv_lib", to: "dv_lib"}, - {from: "hw/dv/sv/dv_utils", to: "dv_utils"}, - {from: "hw/dv/verilator", to: "dv_verilator"}, + {from: "hw/dv/sv/common_ifs", to: "common_ifs"}, + {from: "hw/dv/sv/csr_utils", to: "csr_utils"}, + {from: "hw/dv/sv/dv_lib", to: "dv_lib"}, + {from: "hw/dv/sv/dv_utils", to: "dv_utils"}, + {from: "hw/dv/sv/dv_base_reg", to: "dv_base_reg"}, + {from: "hw/dv/verilator", to: "dv_verilator"}, - {from: "hw/ip/prim", to: "prim"}, - {from: "hw/ip/prim_generic", to: "prim_generic"}, - {from: "hw/ip/prim_xilinx", to: "prim_xilinx"}, + {from: "hw/ip/prim", to: "prim"}, + {from: "hw/ip/prim_generic", to: "prim_generic"}, + {from: "hw/ip/prim_xilinx", to: "prim_xilinx"}, - {from: "hw/lint", to: "lint"}, + {from: "hw/lint", to: "lint"}, - {from: "util/dvsim", to: "dvsim"}, - {from: "util/uvmdvgen", to: "uvmdvgen"}, + {from: "util/dvsim", to: "dvsim"}, + {from: "util/uvmdvgen", to: "uvmdvgen"}, ] } diff --git a/vendor/lowrisc_ip/common_ifs/clk_rst_if.sv b/vendor/lowrisc_ip/common_ifs/clk_rst_if.sv index 31382ecc..394e34f7 100644 --- a/vendor/lowrisc_ip/common_ifs/clk_rst_if.sv +++ b/vendor/lowrisc_ip/common_ifs/clk_rst_if.sv @@ -42,6 +42,7 @@ interface clk_rst_if #( int clk_hi_ps; // half period hi in ps int clk_lo_ps; // half period lo in ps int jitter_chance_pc = 0; // jitter chance in percentage on clock edge - disabled by default + bit sole_clock = 1'b0; // if true, this is the only clock in the system // use IfName as a part of msgs to indicate which clk_rst_vif instance string msg_id = {"clk_rst_if::", IfName}; @@ -129,6 +130,13 @@ interface clk_rst_if #( jitter_chance_pc = jitter_chance; endfunction + // Set whether this is the only clock in the system. If true, various bits of timing randomisation + // are disabled. If there's no other clock to (de)synchronise with, this should not weaken the + // test at all. + function automatic void set_sole_clock(bit is_sole = 1'b1); + sole_clock = is_sole; + endfunction + // start / ungate the clk task automatic start_clk(bit wait_for_posedge = 1'b0); clk_gate = 1'b0; @@ -207,10 +215,27 @@ interface clk_rst_if #( // clk gen initial begin - // start driving clk only after the first por reset assertion - wait_for_reset(.wait_posedge(1'b0)); - #1ps o_clk = 1'b0; - #($urandom_range(0, 10) * 1ps); + // start driving clk only after the first por reset assertion. The fork/join means that we'll + // wait a whole number of clock periods, which means it's possible for the clock to synchronise + // with the "expected" timestamps. + bit done = 1'b0; + fork + begin + wait_for_reset(.wait_posedge(1'b0)); + + // Wait a short time after reset before starting to drive the clock. + #1ps; + o_clk = 1'b0; + + done = 1'b1; + end + while (!done) #(clk_period_ps * 1ps); + join + + // If there might be multiple clocks in the system, wait another (randomised) short time to + // desynchronise. + if (!sole_clock) #($urandom_range(0, clk_period_ps) * 1ps); + forever begin if (recompute) begin clk_hi_ps = clk_period_ps * duty_cycle / 100; diff --git a/vendor/lowrisc_ip/csr_utils/csr_utils.core b/vendor/lowrisc_ip/csr_utils/csr_utils.core index adec6549..0b2cbf41 100644 --- a/vendor/lowrisc_ip/csr_utils/csr_utils.core +++ b/vendor/lowrisc_ip/csr_utils/csr_utils.core @@ -9,9 +9,9 @@ filesets: files_dv: depend: - lowrisc:dv:dv_utils + - lowrisc:dv:dv_base_reg files: - csr_utils_pkg.sv - - csr_excl_item.sv: {is_include_file: true} - csr_seq_lib.sv: {is_include_file: true} file_type: systemVerilogSource diff --git a/vendor/lowrisc_ip/csr_utils/csr_utils_pkg.sv b/vendor/lowrisc_ip/csr_utils/csr_utils_pkg.sv index dcbcf571..bb0252ff 100644 --- a/vendor/lowrisc_ip/csr_utils/csr_utils_pkg.sv +++ b/vendor/lowrisc_ip/csr_utils/csr_utils_pkg.sv @@ -6,6 +6,7 @@ package csr_utils_pkg; // dep packages import uvm_pkg::*; import dv_utils_pkg::*; + import dv_base_reg_pkg::*; // macro includes `include "uvm_macros.svh" @@ -20,9 +21,6 @@ package csr_utils_pkg; bit under_reset = 0; int max_outstanding_accesses = 100; - // global paramters for number of csr tests (including memory test) - parameter uint NUM_CSR_TESTS = 4; - // csr field struct - hold field specific params typedef struct { uvm_reg csr; @@ -31,29 +29,6 @@ package csr_utils_pkg; uint shift; } csr_field_s; - // csr test types - typedef enum bit [NUM_CSR_TESTS-1:0] { - CsrInvalidTest = 4'h0, - // elementary test types - CsrHwResetTest = 4'h1, - CsrRwTest = 4'h2, - CsrBitBashTest = 4'h4, - CsrAliasingTest = 4'h8, - // combinational test types (combinations of the above), used for exclusion tagging - CsrNonInitTests = 4'he, // all but HwReset test - CsrAllTests = 4'hf // all tests - } csr_test_type_e; - - // csr exclusion indications - typedef enum bit [2:0] { - CsrNoExcl = 3'b000, // no exclusions - CsrExclInitCheck = 3'b001, // exclude csr from init val check - CsrExclWriteCheck = 3'b010, // exclude csr from write-read check - CsrExclCheck = 3'b011, // exclude csr from init or write-read check - CsrExclWrite = 3'b100, // exclude csr from write - CsrExclAll = 3'b111 // exclude csr from init or write or write-read check - } csr_excl_type_e; - function automatic void increment_outstanding_access(); outstanding_accesses++; endfunction @@ -252,10 +227,14 @@ package csr_utils_pkg; input uvm_check_e check = UVM_CHECK, input uvm_path_e path = UVM_DEFAULT_PATH, input bit blocking = default_csr_blocking, + input bit backdoor = 0, input uint timeout_ns = default_timeout_ns, input bit predict = 0, input uvm_reg_map map = null); - if (blocking) begin + if (backdoor) begin + csr_poke(csr, value, check); + if (predict) void'(csr.predict(.value(value), .kind(UVM_PREDICT_DIRECT))); + end else if (blocking) begin csr_wr_sub(csr, value, check, path, timeout_ns, map); if (predict) void'(csr.predict(.value(value), .kind(UVM_PREDICT_WRITE))); end else begin @@ -304,14 +283,30 @@ package csr_utils_pkg; join endtask + // backdoor write csr + task automatic csr_poke(input uvm_reg csr, + input uvm_reg_data_t value, + input uvm_check_e check = UVM_CHECK); + uvm_status_e status; + string msg_id = {csr_utils_pkg::msg_id, "::csr_poke"}; + + csr.poke(.status(status), .value(value)); + if (check == UVM_CHECK) begin + `DV_CHECK_EQ(status, UVM_IS_OK, "", error, msg_id) + end + endtask + task automatic csr_rd(input uvm_object ptr, // accept reg or field output uvm_reg_data_t value, input uvm_check_e check = UVM_CHECK, input uvm_path_e path = UVM_DEFAULT_PATH, input bit blocking = default_csr_blocking, + input bit backdoor = 0, input uint timeout_ns = default_timeout_ns, input uvm_reg_map map = null); - if (blocking) begin + if (backdoor) begin + csr_peek(ptr, value, check); + end else if (blocking) begin csr_rd_sub(ptr, value, check, path, timeout_ns, map); end else begin fork @@ -362,10 +357,41 @@ package csr_utils_pkg; join endtask + // backdoor read csr + // uvm_reg::peek() returns a 2-state value, directly get data from hdl path + task automatic csr_peek(input uvm_object ptr, + output uvm_reg_data_t value, + input uvm_check_e check = UVM_CHECK); + string msg_id = {csr_utils_pkg::msg_id, "::csr_peek"}; + csr_field_s csr_or_fld = decode_csr_or_field(ptr); + uvm_reg csr = csr_or_fld.csr; + + if (csr.has_hdl_path()) begin + uvm_hdl_path_concat paths[$]; + + csr.get_full_hdl_path(paths); + foreach (paths[0].slices[i]) begin + uvm_reg_data_t field_val; + if (uvm_hdl_read(paths[0].slices[i].path, field_val)) begin + if (check == UVM_CHECK) `DV_CHECK_EQ($isunknown(value), 0, "", error, msg_id) + value |= field_val << paths[0].slices[i].offset; + end else begin + `uvm_fatal(msg_id, $sformatf("uvm_hdl_read failed for %0s", csr.get_full_name())) + end + end + end else begin + `uvm_fatal(msg_id, $sformatf("No backdoor defined for %0s", csr.get_full_name())) + end + + // if it's field, only return field value + if (csr_or_fld.field != null) value = get_field_val(csr_or_fld.field, value); + endtask + task automatic csr_rd_check(input uvm_object ptr, input uvm_check_e check = UVM_CHECK, input uvm_path_e path = UVM_DEFAULT_PATH, input bit blocking = default_csr_blocking, + input bit backdoor = 0, input uint timeout_ns = default_timeout_ns, input bit compare = 1'b1, input bit compare_vs_ral = 1'b0, @@ -383,10 +409,9 @@ package csr_utils_pkg; uvm_reg_data_t exp; string msg_id = {csr_utils_pkg::msg_id, "::csr_rd_check"}; - increment_outstanding_access(); csr_or_fld = decode_csr_or_field(ptr); - csr_rd(.ptr(ptr), .value(obs), .check(check), .path(path), + csr_rd(.ptr(ptr), .value(obs), .check(check), .path(path), .backdoor(backdoor), .blocking(1), .timeout_ns(timeout_ns), .map(map)); // get mirrored value after read to make sure the read reg access is updated @@ -401,7 +426,6 @@ package csr_utils_pkg; `DV_CHECK_EQ(obs, exp, {"Regname: ", ptr.get_full_name(), " ", err_msg}, error, msg_id) end - decrement_outstanding_access(); end join_none if (blocking) wait fork; @@ -429,6 +453,7 @@ package csr_utils_pkg; input uint spinwait_delay_ns = 0, input uint timeout_ns = default_spinwait_timeout_ns, input compare_op_e compare_op = CompareOpEq, + input bit backdoor = 0, input uvm_verbosity verbosity = UVM_HIGH); fork begin : isolation_fork @@ -437,11 +462,12 @@ package csr_utils_pkg; string msg_id = {csr_utils_pkg::msg_id, "::csr_spinwait"}; csr_or_fld = decode_csr_or_field(ptr); + if (backdoor && spinwait_delay_ns == 0) spinwait_delay_ns = 1; fork while (!under_reset) begin if (spinwait_delay_ns) #(spinwait_delay_ns * 1ns); csr_rd(.ptr(ptr), .value(read_data), .check(check), .path(path), - .blocking(1), .map(map)); + .blocking(1), .map(map), .backdoor(backdoor)); `uvm_info(msg_id, $sformatf("ptr %0s == 0x%0h", ptr.get_full_name(), read_data), verbosity) case (compare_op) @@ -566,8 +592,6 @@ package csr_utils_pkg; join endtask : mem_wr_sub - `include "csr_excl_item.sv" - // Fields could be excluded from writes & reads - This function zeros out the excluded fields function automatic uvm_reg_data_t get_mask_excl_fields(uvm_reg csr, csr_excl_type_e csr_excl_type, diff --git a/vendor/lowrisc_ip/csr_utils/csr_excl_item.sv b/vendor/lowrisc_ip/dv_base_reg/csr_excl_item.sv similarity index 100% rename from vendor/lowrisc_ip/csr_utils/csr_excl_item.sv rename to vendor/lowrisc_ip/dv_base_reg/csr_excl_item.sv diff --git a/vendor/lowrisc_ip/dv_lib/dv_base_mem.sv b/vendor/lowrisc_ip/dv_base_reg/dv_base_mem.sv similarity index 100% rename from vendor/lowrisc_ip/dv_lib/dv_base_mem.sv rename to vendor/lowrisc_ip/dv_base_reg/dv_base_mem.sv diff --git a/vendor/lowrisc_ip/dv_base_reg/dv_base_reg.core b/vendor/lowrisc_ip/dv_base_reg/dv_base_reg.core new file mode 100644 index 00000000..ff746ae5 --- /dev/null +++ b/vendor/lowrisc_ip/dv_base_reg/dv_base_reg.core @@ -0,0 +1,25 @@ +CAPI=2: +# Copyright lowRISC contributors. +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 +name: "lowrisc:dv:dv_base_reg" +description: "DV base reg/mem library" + +filesets: + files_dv: + depend: + - lowrisc:dv:dv_utils + files: + - dv_base_reg_pkg.sv + - csr_excl_item.sv: {is_include_file: true} + - dv_base_reg_field.sv: {is_include_file: true} + - dv_base_reg.sv: {is_include_file: true} + - dv_base_mem.sv: {is_include_file: true} + - dv_base_reg_block.sv: {is_include_file: true} + - dv_base_reg_map.sv: {is_include_file: true} + file_type: systemVerilogSource + +targets: + default: + filesets: + - files_dv diff --git a/vendor/lowrisc_ip/dv_lib/dv_base_reg.sv b/vendor/lowrisc_ip/dv_base_reg/dv_base_reg.sv similarity index 90% rename from vendor/lowrisc_ip/dv_lib/dv_base_reg.sv rename to vendor/lowrisc_ip/dv_base_reg/dv_base_reg.sv index 39139403..3ad45eeb 100644 --- a/vendor/lowrisc_ip/dv_lib/dv_base_reg.sv +++ b/vendor/lowrisc_ip/dv_base_reg/dv_base_reg.sv @@ -4,6 +4,11 @@ // // base register class which will be used to generate the reg class dv_base_reg extends uvm_reg; + // external reg doesn't have storage in reg module, which may connect to some combinational logic + // hence, backdoor write isn't available + local bit is_ext_reg; + + local dv_base_reg locked_regs[$]; function new(string name = "", int unsigned n_bits, @@ -11,9 +16,6 @@ class dv_base_reg extends uvm_reg; super.new(name, n_bits, has_coverage); endfunction : new - - local dv_base_reg locked_regs[$]; - function void get_dv_base_reg_fields(ref dv_base_reg_field dv_fields[$]); uvm_reg_field ral_fields[$]; get_fields(ral_fields); @@ -80,4 +82,12 @@ class dv_base_reg extends uvm_reg; end endtask + virtual function void set_is_ext_reg(bit is_ext); + is_ext_reg = is_ext; + endfunction + + virtual function bit get_is_ext_reg(); + return is_ext_reg; + endfunction + endclass diff --git a/vendor/lowrisc_ip/dv_lib/dv_base_reg_block.sv b/vendor/lowrisc_ip/dv_base_reg/dv_base_reg_block.sv similarity index 96% rename from vendor/lowrisc_ip/dv_lib/dv_base_reg_block.sv rename to vendor/lowrisc_ip/dv_base_reg/dv_base_reg_block.sv index 5f72678d..c0bbcc62 100644 --- a/vendor/lowrisc_ip/dv_lib/dv_base_reg_block.sv +++ b/vendor/lowrisc_ip/dv_base_reg/dv_base_reg_block.sv @@ -14,7 +14,7 @@ class dv_base_reg_block extends uvm_reg_block; // provide build function to supply base addr virtual function void build(uvm_reg_addr_t base_addr, - csr_utils_pkg::csr_excl_item csr_excl = null); + csr_excl_item csr_excl = null); `uvm_fatal(`gfn, "this method is not supposed to be called directly!") endfunction diff --git a/vendor/lowrisc_ip/dv_lib/dv_base_reg_field.sv b/vendor/lowrisc_ip/dv_base_reg/dv_base_reg_field.sv similarity index 80% rename from vendor/lowrisc_ip/dv_lib/dv_base_reg_field.sv rename to vendor/lowrisc_ip/dv_base_reg/dv_base_reg_field.sv index 021d1edf..6bcb400f 100644 --- a/vendor/lowrisc_ip/dv_lib/dv_base_reg_field.sv +++ b/vendor/lowrisc_ip/dv_base_reg/dv_base_reg_field.sv @@ -11,11 +11,15 @@ class dv_base_reg_field extends uvm_reg_field; // when use UVM_PREDICT_WRITE and the CSR access is WO, this function will return the default // val of the register, rather than the written value + // TODO, need to handle predict value when backdoor write happens WO reg + // 1. for read, design ties the read data to default value + // 2. when backdoor write updates internal reg, backdoor read can get the written value, but + // frontdoor read always returns the default value. virtual function uvm_reg_data_t XpredictX(uvm_reg_data_t cur_val, uvm_reg_data_t wr_val, uvm_reg_map map); - if (get_access(map) == "WO") return cur_val; + if (get_access(map) == "WO") return get_reset(); else return super.XpredictX(cur_val, wr_val, map); endfunction diff --git a/vendor/lowrisc_ip/dv_lib/dv_base_reg_map.sv b/vendor/lowrisc_ip/dv_base_reg/dv_base_reg_map.sv similarity index 100% rename from vendor/lowrisc_ip/dv_lib/dv_base_reg_map.sv rename to vendor/lowrisc_ip/dv_base_reg/dv_base_reg_map.sv diff --git a/vendor/lowrisc_ip/dv_base_reg/dv_base_reg_pkg.sv b/vendor/lowrisc_ip/dv_base_reg/dv_base_reg_pkg.sv new file mode 100644 index 00000000..a5018ce3 --- /dev/null +++ b/vendor/lowrisc_ip/dv_base_reg/dv_base_reg_pkg.sv @@ -0,0 +1,49 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +package dv_base_reg_pkg; + // dep packages + import uvm_pkg::*; + import dv_utils_pkg::*; + + // macro includes + `include "uvm_macros.svh" + `include "dv_macros.svh" + + // global paramters for number of csr tests (including memory test) + parameter uint NUM_CSR_TESTS = 4; + + // csr exclusion indications + typedef enum bit [2:0] { + CsrNoExcl = 3'b000, // no exclusions + CsrExclInitCheck = 3'b001, // exclude csr from init val check + CsrExclWriteCheck = 3'b010, // exclude csr from write-read check + CsrExclCheck = 3'b011, // exclude csr from init or write-read check + CsrExclWrite = 3'b100, // exclude csr from write + CsrExclAll = 3'b111 // exclude csr from init or write or write-read check + } csr_excl_type_e; + + // csr test types + typedef enum bit [NUM_CSR_TESTS-1:0] { + CsrInvalidTest = 4'h0, + // elementary test types + CsrHwResetTest = 4'h1, + CsrRwTest = 4'h2, + CsrBitBashTest = 4'h4, + CsrAliasingTest = 4'h8, + // combinational test types (combinations of the above), used for exclusion tagging + CsrNonInitTests = 4'he, // all but HwReset test + CsrAllTests = 4'hf // all tests + } csr_test_type_e; + + // package sources + // base ral + `include "csr_excl_item.sv" + `include "dv_base_reg_field.sv" + `include "dv_base_reg.sv" + `include "dv_base_mem.sv" + `include "dv_base_reg_block.sv" + `include "dv_base_reg_map.sv" + +endpackage diff --git a/vendor/lowrisc_ip/dv_lib/dv_base_env_cfg.sv b/vendor/lowrisc_ip/dv_lib/dv_base_env_cfg.sv index 1360ff64..0512e1e8 100644 --- a/vendor/lowrisc_ip/dv_lib/dv_base_env_cfg.sv +++ b/vendor/lowrisc_ip/dv_lib/dv_base_env_cfg.sv @@ -15,6 +15,7 @@ class dv_base_env_cfg #(type RAL_T = dv_base_reg_block) extends uvm_object; // reg model & q of valid csr addresses RAL_T ral; + dv_base_reg_block ral_models[$]; bit [TL_AW-1:0] csr_addrs[$]; addr_range_t mem_ranges[$]; @@ -64,6 +65,7 @@ class dv_base_env_cfg #(type RAL_T = dv_base_reg_block) extends uvm_object; ral = RAL_T::type_id::create("ral"); ral.build(this.csr_base_addr, null); apply_ral_fixes(); + ral_models.push_back(ral); end endfunction diff --git a/vendor/lowrisc_ip/dv_lib/dv_base_vseq.sv b/vendor/lowrisc_ip/dv_lib/dv_base_vseq.sv index 6b0e8c5b..7cade3bf 100644 --- a/vendor/lowrisc_ip/dv_lib/dv_base_vseq.sv +++ b/vendor/lowrisc_ip/dv_lib/dv_base_vseq.sv @@ -71,7 +71,9 @@ class dv_base_vseq #(type RAL_T = dv_base_reg_block, cfg.clk_rst_vif.apply_reset(); csr_utils_pkg::reset_deasserted(); end - if (cfg.has_ral) ral.reset(kind); + if (cfg.has_ral) begin + foreach (cfg.ral_models[i]) cfg.ral_models[i].reset(kind); + end endtask virtual task wait_for_reset(string reset_kind = "HARD", @@ -167,7 +169,7 @@ class dv_base_vseq #(type RAL_T = dv_base_reg_block, // run write-only sequence to randomize the csr values m_csr_write_seq = csr_write_seq::type_id::create("m_csr_write_seq"); - m_csr_write_seq.models.push_back(ral); + m_csr_write_seq.models = cfg.ral_models; m_csr_write_seq.set_csr_excl_item(csr_excl); m_csr_write_seq.external_checker = cfg.en_scb; if (!enable_asserts_in_hw_reset_rand_wr) $assertoff; @@ -184,7 +186,7 @@ class dv_base_vseq #(type RAL_T = dv_base_reg_block, // create base csr seq and pass our ral m_csr_seq = csr_base_seq::type_id::create("m_csr_seq"); m_csr_seq.num_test_csrs = num_test_csrs; - m_csr_seq.models.push_back(ral); + m_csr_seq.models = cfg.ral_models; m_csr_seq.set_csr_excl_item(csr_excl); m_csr_seq.external_checker = cfg.en_scb; m_csr_seq.start(null); diff --git a/vendor/lowrisc_ip/dv_lib/dv_lib.core b/vendor/lowrisc_ip/dv_lib/dv_lib.core index 73c7fe16..2d18f535 100644 --- a/vendor/lowrisc_ip/dv_lib/dv_lib.core +++ b/vendor/lowrisc_ip/dv_lib/dv_lib.core @@ -10,15 +10,10 @@ filesets: depend: - lowrisc:dv:dv_utils - lowrisc:dv:csr_utils + - lowrisc:dv:dv_base_reg files: - dv_lib_pkg.sv - - dv_base_reg_field.sv: {is_include_file: true} - - dv_base_reg.sv: {is_include_file: true} - - dv_base_mem.sv: {is_include_file: true} - - dv_base_reg_block.sv: {is_include_file: true} - - dv_base_reg_map.sv: {is_include_file: true} - - dv_base_agent_cfg.sv: {is_include_file: true} - dv_base_agent_cov.sv: {is_include_file: true} - dv_base_monitor.sv: {is_include_file: true} diff --git a/vendor/lowrisc_ip/dv_lib/dv_lib_pkg.sv b/vendor/lowrisc_ip/dv_lib/dv_lib_pkg.sv index 0036c4ec..034b4995 100644 --- a/vendor/lowrisc_ip/dv_lib/dv_lib_pkg.sv +++ b/vendor/lowrisc_ip/dv_lib/dv_lib_pkg.sv @@ -8,6 +8,7 @@ package dv_lib_pkg; import top_pkg::*; import dv_utils_pkg::*; import csr_utils_pkg::*; + import dv_base_reg_pkg::*; // macro includes `include "uvm_macros.svh" @@ -17,13 +18,6 @@ package dv_lib_pkg; string msg_id = "dv_lib_pkg"; // package sources - // base ral - `include "dv_base_reg_field.sv" - `include "dv_base_reg.sv" - `include "dv_base_mem.sv" - `include "dv_base_reg_block.sv" - `include "dv_base_reg_map.sv" - // base agent `include "dv_base_agent_cfg.sv" `include "dv_base_agent_cov.sv" diff --git a/vendor/lowrisc_ip/dvsim/Deploy.py b/vendor/lowrisc_ip/dvsim/Deploy.py index 7f958f41..b0dde6ac 100644 --- a/vendor/lowrisc_ip/dvsim/Deploy.py +++ b/vendor/lowrisc_ip/dvsim/Deploy.py @@ -257,8 +257,9 @@ class Deploy(): def set_status(self): self.status = 'P' if self.dry_run is False: + seen_fail_pattern = False for fail_pattern in self.fail_patterns: - # Return error messege with the following 4 lines. + # Return error message with the following 4 lines. grep_cmd = "grep -m 1 -A 4 -E \'" + fail_pattern + "\' " + self.log (status, rslt) = subprocess.getstatusoutput(grep_cmd) if rslt: @@ -266,12 +267,13 @@ class Deploy(): self.fail_msg += msg log.log(VERBOSE, msg) self.status = 'F' + seen_fail_pattern = True break # If fail patterns were not encountered, but the job returned with non-zero exit code # for whatever reason, then show the last 10 lines of the log as the failure message, # which might help with the debug. - if self.process.returncode != 0 and not self.fail_msg: + if self.process.returncode != 0 and not seen_fail_pattern: msg = "Last 10 lines of the log:
\n" self.fail_msg += msg log.log(VERBOSE, msg) diff --git a/vendor/lowrisc_ip/dvsim/SimCfg.py b/vendor/lowrisc_ip/dvsim/SimCfg.py index 3f8a6f09..e6a23618 100644 --- a/vendor/lowrisc_ip/dvsim/SimCfg.py +++ b/vendor/lowrisc_ip/dvsim/SimCfg.py @@ -90,8 +90,7 @@ class SimCfg(FlowCfg): self.tool = args.tool self.build_opts = [] self.build_opts.extend(args.build_opts) - self.en_build_modes = [] - self.en_build_modes.extend(args.build_modes) + self.en_build_modes = args.build_modes.copy() self.run_opts = [] self.run_opts.extend(args.run_opts) self.en_run_modes = [] @@ -223,7 +222,7 @@ class SimCfg(FlowCfg): # Use the default build mode for tests that do not specify it if not hasattr(self, "build_mode"): - setattr(self, "build_mode", "default") + self.build_mode = 'default' self._process_exports() @@ -257,16 +256,12 @@ class SimCfg(FlowCfg): def _create_objects(self): # Create build and run modes objects - build_modes = Modes.create_modes(BuildModes, - getattr(self, "build_modes")) - setattr(self, "build_modes", build_modes) - - run_modes = Modes.create_modes(RunModes, getattr(self, "run_modes")) - setattr(self, "run_modes", run_modes) + self.build_modes = Modes.create_modes(BuildModes, self.build_modes) + self.run_modes = Modes.create_modes(RunModes, self.run_modes) # Walk through build modes enabled on the CLI and append the opts for en_build_mode in self.en_build_modes: - build_mode_obj = Modes.find_mode(en_build_mode, build_modes) + build_mode_obj = Modes.find_mode(en_build_mode, self.build_modes) if build_mode_obj is not None: self.build_opts.extend(build_mode_obj.build_opts) self.run_opts.extend(build_mode_obj.run_opts) @@ -278,7 +273,7 @@ class SimCfg(FlowCfg): # Walk through run modes enabled on the CLI and append the opts for en_run_mode in self.en_run_modes: - run_mode_obj = Modes.find_mode(en_run_mode, run_modes) + run_mode_obj = Modes.find_mode(en_run_mode, self.run_modes) if run_mode_obj is not None: self.run_opts.extend(run_mode_obj.run_opts) else: @@ -288,8 +283,7 @@ class SimCfg(FlowCfg): sys.exit(1) # Create tests from given list of items - tests = Tests.create_tests(getattr(self, "tests"), self) - setattr(self, "tests", tests) + self.tests = Tests.create_tests(self.tests, self) # Regressions # Parse testplan if provided. @@ -299,9 +293,8 @@ class SimCfg(FlowCfg): self.regressions.extend(self.testplan.get_milestone_regressions()) # Create regressions - regressions = Regressions.create_regressions( - getattr(self, "regressions"), self, tests) - setattr(self, "regressions", regressions) + self.regressions = Regressions.create_regressions(self.regressions, + self, self.tests) def _print_list(self): for list_item in self.list_items: @@ -417,6 +410,43 @@ class SimCfg(FlowCfg): create_link_dirs_cmd) sys.exit(1) + def _expand_run_list(self, build_map): + '''Generate a list of tests to be run + + For each test in tests, we add it test.reseed times. The ordering is + interleaved so that we run through all of the tests as soon as + possible. If there are multiple tests and they have different reseed + values, they are "fully interleaved" at the start (so if there are + tests A, B with reseed values of 5 and 2, respectively, then the list + will be ABABAAA). + + build_map is a dictionary from build name to a CompileSim object. Each + test is added to the CompileSim item that it depends on (signifying + that the test should be built once the build on which it depends is + done). + + cfg is a SimCfg object, passed to the RunTest constructor. + + ''' + tagged = [] + for test in self.run_list: + for idx in range(test.reseed): + tagged.append((idx, + test, + RunTest(idx, test, self))) + + # Stably sort the tagged list by the 1st coordinate + tagged.sort(key=lambda x: x[0]) + + # Now iterate over it again, adding tests to build_map (in the + # interleaved order) and collecting up the RunTest objects. + runs = [] + for _, test, run in tagged: + build_map[test.build_mode].sub.append(run) + runs.append(run) + + return runs + def _create_deploy_objects(self): '''Create deploy objects from the build and run lists. ''' @@ -431,18 +461,12 @@ class SimCfg(FlowCfg): builds.append(item) build_map[build] = item - runs = [] - for test in self.run_list: - for num in range(test.reseed): - item = RunTest(num, test, self) - if self.build_only is False: - build_map[test.build_mode].sub.append(item) - runs.append(item) - self.builds = builds - self.runs = runs + self.runs = ([] + if self.build_only + else self._expand_run_list(build_map)) if self.run_only is True: - self.deploy = runs + self.deploy = self.runs else: self.deploy = builds @@ -555,8 +579,7 @@ class SimCfg(FlowCfg): # Add path to testplan. if hasattr(self, "testplan_doc_path"): - testplan = "https://" + self.doc_server + '/' + getattr( - self, "testplan_doc_path") + testplan = "https://" + self.doc_server + '/' + self.testplan_doc_path else: testplan = "https://" + self.doc_server + '/' + self.rel_path testplan = testplan.replace("/dv", "/doc/dv_plan/#testplan") @@ -583,8 +606,7 @@ class SimCfg(FlowCfg): # Link the dashboard page using "cov_report_page" value. if hasattr(self, "cov_report_page"): results_str += "\n### [Coverage Dashboard]" - results_str += "({})\n\n".format( - getattr(self, "cov_report_page")) + results_str += "({})\n\n".format(self.cov_report_page) results_str += self.cov_report_deploy.cov_results self.results_summary[ "Coverage"] = self.cov_report_deploy.cov_total diff --git a/vendor/lowrisc_ip/dvsim/dvsim.py b/vendor/lowrisc_ip/dvsim/dvsim.py index 1d601eab..5e1cb5ec 100755 --- a/vendor/lowrisc_ip/dvsim/dvsim.py +++ b/vendor/lowrisc_ip/dvsim/dvsim.py @@ -2,14 +2,21 @@ # Copyright lowRISC contributors. # Licensed under the Apache License, Version 2.0, see LICENSE for details. # SPDX-License-Identifier: Apache-2.0 -""" -dvsim is a command line tool to deploy regressions for design verification. It uses hjson as the -format for specifying what to build and run. It is an end-to-end regression manager that can deploy -multiple builds (where some tests might need different set of compile time options requiring a -uniquely build sim executable) in parallel followed by tests in parallel using the load balancer -of your choice. dvsim is built to be tool-agnostic so that you can easily switch between tools -available at your disposal. dvsim uses fusesoc as the starting step to resolve all inter-package -dependencies and provide us with a filelist that will be consumed by the sim tool. +"""dvsim is a command line tool to deploy ASIC tool flows such as regressions +for design verification (DV), formal property verification (FPV), linting and +synthesis. + +It uses hjson as the format for specifying what to build and run. It is an +end-to-end regression manager that can deploy multiple builds (where some tests +might need different set of compile time options requiring a uniquely build sim +executable) in parallel followed by tests in parallel using the load balancer +of your choice. + +dvsim is built to be tool-agnostic so that you can easily switch between the +tools at your disposal. dvsim uses fusesoc as the starting step to resolve all +inter-package dependencies and provide us with a filelist that will be consumed +by the sim tool. + """ import argparse @@ -18,6 +25,7 @@ import logging as log import os import subprocess import sys +import textwrap from signal import SIGINT, signal import Deploy @@ -72,6 +80,39 @@ def resolve_scratch_root(arg_scratch_root): return (arg_scratch_root) +def read_max_parallel(arg): + '''Take value for --max-parallel as an integer''' + try: + int_val = int(arg) + if int_val <= 0: + raise ValueError('bad value') + return int_val + + except ValueError: + raise argparse.ArgumentTypeError('Bad argument for --max-parallel ' + '({!r}): must be a positive integer.' + .format(arg)) + + +def resolve_max_parallel(arg): + '''Pick a value of max_parallel, defaulting to 16 or $DVSIM_MAX_PARALLEL''' + if arg is not None: + assert arg > 0 + return arg + + from_env = os.environ.get('DVSIM_MAX_PARALLEL') + if from_env is not None: + try: + return read_max_parallel(from_env) + except argparse.ArgumentTypeError: + log.warning('DVSIM_MAX_PARALLEL environment variable has value ' + '{!r}, which is not a positive integer. Using default ' + 'value (16).' + .format(from_env)) + + return 16 + + def resolve_branch(branch): '''Choose a branch name for output files @@ -138,301 +179,304 @@ def sigint_handler(signal_received, frame): exit(1) -def main(): +def wrapped_docstring(): + '''Return a text-wrapped version of the module docstring''' + paras = [] + para = [] + for line in __doc__.strip().split('\n'): + line = line.strip() + if not line: + if para: + paras.append('\n'.join(para)) + para = [] + else: + para.append(line) + if para: + paras.append('\n'.join(para)) + + return '\n\n'.join(textwrap.fill(p) for p in paras) + + +def parse_args(): parser = argparse.ArgumentParser( - description=__doc__, + description=wrapped_docstring(), formatter_class=argparse.RawDescriptionHelpFormatter) parser.add_argument("cfg", metavar="", help="""Configuration hjson file.""") - parser.add_argument("-i", - "--items", - nargs="*", - default=["sanity"], - metavar="regr1, regr2, regr3|test1, test2, test3, ...", - help="""Indicate which regressions or tests to run.""") - - parser.add_argument( - "-l", - "--list", - nargs="*", - choices=_LIST_CATEGORIES, - metavar="build_modes|run_modes|tests|regressions", - help= - """List the available build_modes / run_modes / tests / regressions for use.""" - ) - - parser.add_argument("-t", - "--tool", - metavar="vcs|xcelium|ascentlint|veriblelint|dc|...", - help="Override the tool that is set in hjson file") - - parser.add_argument( - "--select-cfgs", - nargs="*", - metavar="cfg1, cfg2, cfg3, ...", - help="""Specifies which cfg(s) of the master cfg shall be processed. - If this switch is not specified, dvsim will process all cfgs specified in - the master cfg list.""") - - parser.add_argument( - "-sr", - "--scratch-root", - metavar="path", - help="""root scratch directory path where all build / run drectories go; - by default, the tool will create the {scratch_path} = {scratch_root}/{dut} - directory if it doesn't already exist; under {scratch_path}, there will be - {compile_set} set of directories where all the build outputs go and - {test_name} set of directories where the test outputs go""" - ) - - parser.add_argument("-pr", - "--proj-root", - metavar="path", - help="""Specify the root directory of the project. - If this option is not passed, the tool will assume that this is - a local GitHub repository and will attempt to automatically find - the root directory.""") - - parser.add_argument( - "-br", - "--branch", - metavar="", - help= - """This variable is used to construct the scratch path directory name. If not - specified, it defaults to the GitHub branch name. The idea is to uniquefy the - scratch paths between different branches.""") - - parser.add_argument( - "-bo", - "--build-opts", - nargs="+", - default=[], - metavar="", - help="""Pass additional build options over the command line; - note that if there are multiple compile sets identified to be built, - these options will be passed on to all of them""") - - parser.add_argument( - "-bm", - "--build-modes", - nargs="+", - default=[], - metavar="", - help="""Set build modes on the command line for all tests run as a part - of the regression.""") - - parser.add_argument( - "-ro", - "--run-opts", - nargs="+", - default=[], - metavar="", - help="""Pass additional run time options over the command line; - these options will be passed on to all tests scheduled to be run""" - ) - - parser.add_argument( - "-rm", - "--run-modes", - nargs="+", - default=[], - metavar="", - help="""Set run modes on the command line for all tests run as a part - of the regression.""") - - parser.add_argument( - "-bu", - "--build-unique", - action='store_true', - help= - """By default, under the {scratch} directory, there is a {compile_set} - directory created where the build output goes; this can be - uniquified by appending the current timestamp. This is suitable - for the case when a test / regression already running and you want - to run something else from a different terminal without affecting - the previous one""") - - parser.add_argument( - "--build-only", - action='store_true', - help="Only build the simulation executables for the givem items.") - - parser.add_argument( - "--run-only", - action='store_true', - help="Assume sim exec is available and proceed to run step") - - parser.add_argument( - "-s", - "--seeds", - nargs="+", - default=[], - metavar="seed0 seed1 ...", - help= - """Run tests with a specific seeds. Note that these specific seeds are applied to - items being run in the order they are passed.""") - - parser.add_argument( - "--fixed-seed", - type=int, - help= - """Run all items with a fixed seed value. This option enforces --reseed 1.""" - ) - - parser.add_argument( - "-r", - "--reseed", - type=int, - metavar="N", - help="""Repeat tests with N iterations with different seeds""") - - parser.add_argument("-rx", - "--reseed-multiplier", - type=int, - default=1, - metavar="N", - help="""Multiplier for existing reseed values.""") - - parser.add_argument("-w", - "--waves", - action='store_true', - help="Enable dumping of waves") - - parser.add_argument("-d", - "--dump", - choices=["fsdb", "shm", "vpd"], - help=("Format to dump waves for simulation. If Verdi " - "is installed (detected by searching PATH) this " - "defaults to fsdb. Otherwise, defaults to shm " - "for Xcelium or vpd for VCS.")) - - parser.add_argument("-mw", - "--max-waves", - type=int, - default=5, - metavar="N", - help="""Enable dumping of waves for at most N tests; - this includes tests scheduled for run AND automatic rerun""" - ) - - parser.add_argument("-c", - "--cov", - action='store_true', - help="turn on coverage collection") - - parser.add_argument( - "--cov-merge-previous", - action='store_true', - help="""Applicable when --cov switch is enabled. If a previous cov - database directory exists, this switch will cause it to be merged with - the current cov database.""") - - parser.add_argument( - "--cov-analyze", - action='store_true', - help="Analyze the coverage from the last regression result.") - - parser.add_argument("-p", - "--profile", - nargs='?', - const='time', - choices=["time", "mem"], - help="Turn on simulation profiling") - - parser.add_argument("--xprop-off", - action='store_true', - help="Turn off Xpropagation") - - parser.add_argument("--job-prefix", - default="", - metavar="job-prefix", - help="Job prefix before deploying the tool commands.") - - parser.add_argument("--purge", - action='store_true', - help="Clean the scratch directory before running.") - - parser.add_argument( - "-mo", - "--max-odirs", - type=int, - default=5, - metavar="N", - help="""When tests are run, the older runs are backed up. This switch - limits the number of backup directories being maintained.""") - - parser.add_argument( - "--no-rerun", - action='store_true', - help= - """By default, failing tests will be automatically be rerun with waves; - this option will prevent the rerun from being triggered""") - - parser.add_argument("-v", - "--verbosity", - default="l", - choices=["n", "l", "m", "h", "d"], - help="""Set verbosity to none/low/medium/high/debug; - This will override any setting added to any of the hjson files - used for config""") - - parser.add_argument( - "--verbose", - nargs="?", - choices=['default', 'debug'], - default=None, - const="default", - metavar="debug", - help="""Print verbose dvsim tool messages. If 'debug' is passed, then the - volume of messages is ven higher.""") - parser.add_argument("--version", action='store_true', help="Print version and exit") - parser.add_argument( - "-n", - "--dry-run", - action='store_true', - help= - "Print dvsim tool messages only, without actually running any command") + parser.add_argument("--tool", "-t", + default="", + help=("Explicitly set the tool to use. This is " + "optional for running simulations (where it can " + "be set in an .hjson file), but is required for " + "other flows. Possible tools include: vcs, " + "xcelium, ascentlint, verible, dc.")) - parser.add_argument( - "--map-full-testplan", - action='store_true', - help="Force complete testplan annotated results to be shown at the end." - ) + parser.add_argument("--list", "-l", + nargs="*", + metavar='CAT', + choices=_LIST_CATEGORIES, + help=('Parse the the given .hjson config file, list ' + 'the things that can be run, then exit. The ' + 'list can be filtered with a space-separated ' + 'of categories from: {}.' + .format(', '.join(_LIST_CATEGORIES)))) - parser.add_argument( - "--publish", - action='store_true', - help="Publish results to the reports.opentitan.org web server.") + whatg = parser.add_argument_group('Choosing what to run') - parser.add_argument( - "-pi", - "--print-interval", - type=int, - default=10, - metavar="N", - help="""Interval in seconds. Print status every N seconds.""") + whatg.add_argument("-i", + "--items", + nargs="*", + default=["sanity"], + help=('Specify the regressions or tests to run. ' + 'Defaults to "sanity", but can be a ' + 'space separated list of test or regression ' + 'names.')) - parser.add_argument( - "-mp", - "--max-parallel", - type=int, - default=16, - metavar="N", - help="""Run only upto a fixed number of builds/tests at a time.""") + whatg.add_argument("--select-cfgs", + nargs="*", + metavar="CFG", + help=('The .hjson file is a master config. Only run ' + 'the given configs from it. If this argument is ' + 'not used, dvsim will process all configs listed ' + 'in a master config.')) - parser.add_argument( - "--local", - action='store_true', - help= - """Deploy builds and runs on the local workstation instead of the compute farm. - Support for this has not been added yet.""") + disg = parser.add_argument_group('Dispatch options') + + disg.add_argument("--job-prefix", + default="", + metavar="PFX", + help=('Prepend this string when running each tool ' + 'command.')) + + disg.add_argument("--max-parallel", "-mp", + type=read_max_parallel, + metavar="N", + help=('Run only up to N builds/tests at a time. ' + 'Default value 16, unless the DVSIM_MAX_PARALLEL ' + 'environment variable is set, in which case that ' + 'is used.')) + + pathg = parser.add_argument_group('File management') + + pathg.add_argument("--scratch-root", "-sr", + metavar="PATH", + help=('Destination for build / run directories. If not ' + 'specified, uses the path in the SCRATCH_ROOT ' + 'environment variable, if set, or ./scratch ' + 'otherwise.')) + + pathg.add_argument("--proj-root", "-pr", + metavar="PATH", + help=('The root directory of the project. If not ' + 'specified, dvsim will search for a git ' + 'repository containing the current directory.')) + + pathg.add_argument("--branch", "-br", + metavar='B', + help=('By default, dvsim creates files below ' + '{scratch-root}/{dut}.{flow}.{tool}/{branch}. ' + 'If --branch is not specified, dvsim assumes the ' + 'current directory is a git repository and uses ' + 'the name of the current branch.')) + + pathg.add_argument("--max-odirs", "-mo", + type=int, + default=5, + metavar="N", + help=('When tests are run, older runs are backed ' + 'up. Discard all but the N most recent (defaults ' + 'to 5).')) + + pathg.add_argument("--purge", + action='store_true', + help="Clean the scratch directory before running.") + + buildg = parser.add_argument_group('Options for building') + + buildg.add_argument("--build-only", + action='store_true', + help=('Stop after building executables for the given ' + 'items.')) + + buildg.add_argument("--build-unique", "-bu", + action='store_true', + help=('Append a timestamp to the directory in which ' + 'files are built. This is suitable for the case ' + 'when another test is already running and you ' + 'want to run something else from a different ' + 'terminal without affecting it.')) + + buildg.add_argument("--build-opts", "-bo", + nargs="+", + default=[], + metavar="OPT", + help=('Additional options passed on the command line ' + 'each time a build tool is run.')) + + buildg.add_argument("--build-modes", "-bm", + nargs="+", + default=[], + metavar="MODE", + help=('The options for each build_mode in this list ' + 'are applied to all build and run targets.')) + + rung = parser.add_argument_group('Options for running') + + rung.add_argument("--run-only", + action='store_true', + help=('Skip the build step (assume that simulation ' + 'executables have already been built).')) + + rung.add_argument("--run-opts", "-ro", + nargs="+", + default=[], + metavar="OPT", + help=('Additional options passed on the command line ' + 'each time a test is run.')) + + rung.add_argument("--run-modes", "-rm", + nargs="+", + default=[], + metavar="MODE", + help=('The options for each run_mode in this list are ' + 'applied to each simulation run.')) + + rung.add_argument("--profile", "-p", + choices=['time', 'mem'], + metavar="P", + help=('Turn on simulation profiling (where P is time ' + 'or mem).')) + + rung.add_argument("--xprop-off", + action='store_true', + help="Turn off X-propagation in simulation.") + + rung.add_argument("--no-rerun", + action='store_true', + help=("Disable the default behaviour, where failing " + "tests are automatically rerun with waves " + "enabled.")) + + rung.add_argument("--verbosity", "-v", + default="l", + choices=['n', 'l', 'm', 'h', 'd'], + metavar='V', + help=('Set UVM verbosity to none (n), low (l; the ' + 'default), medium (m), high (h) or debug (d). ' + 'This overrides any setting in the config files.')) + + seedg = parser.add_argument_group('Test seeds') + + seedg.add_argument("--seeds", "-s", + nargs="+", + default=[], + metavar="S", + help=('A list of seeds for tests. Note that these ' + 'specific seeds are applied to items being run ' + 'in the order they are passed.')) + + seedg.add_argument("--fixed-seed", + type=int, + metavar='S', + help=('Run all items with the seed S. This implies ' + '--reseed 1.')) + + seedg.add_argument("--reseed", "-r", + type=int, + metavar="N", + help=('Override any reseed value in the test ' + 'configuration and run each test N times, with ' + 'a new seed each time.')) + + seedg.add_argument("--reseed-multiplier", "-rx", + type=int, + default=1, + metavar="N", + help=('Scale each reseed value in the test ' + 'configuration by N. This allows e.g. running ' + 'the tests 10 times as much as normal while ' + 'maintaining the ratio of numbers of runs ' + 'between different tests.')) + + waveg = parser.add_argument_group('Dumping waves') + + waveg.add_argument("--waves", "-w", + action='store_true', + help="Enable dumping of waves") + + waveg.add_argument("-d", + "--dump", + choices=["fsdb", "shm", "vpd"], + help=("Format to dump waves for simulation. The default " + "format depends on the tool. With VCS, this " + "defaults to fsdb if Verdi is installed, else " + "vpd. With Xcelium, defaults to shm.")) + + waveg.add_argument("--max-waves", "-mw", + type=int, + default=5, + metavar="N", + help=('Only dump waves for the first N tests run. This ' + 'includes both tests scheduled for run and those ' + 'that are automatically rerun.')) + + covg = parser.add_argument_group('Generating simulation coverage') + + covg.add_argument("--cov", "-c", + action='store_true', + help="Enable collection of coverage data.") + + covg.add_argument("--cov-merge-previous", + action='store_true', + help=('Only applicable with --cov. Merge any previous ' + 'coverage database directory with the new ' + 'coverage database.')) + + covg.add_argument("--cov-analyze", + action='store_true', + help=('Rather than building or running any tests, ' + 'analyze the coverage from the last run.')) + + pubg = parser.add_argument_group('Generating and publishing results') + + pubg.add_argument("--map-full-testplan", + action='store_true', + help=("Show complete testplan annotated results " + "at the end.")) + + pubg.add_argument("--publish", + action='store_true', + help="Publish results to reports.opentitan.org.") + + dvg = parser.add_argument_group('Controlling DVSim itself') + + dvg.add_argument("--print-interval", "-pi", + type=int, + default=10, + metavar="N", + help="Print status every N seconds.") + + dvg.add_argument("--verbose", + nargs="?", + choices=['default', 'debug'], + default=None, + const="default", + metavar="D", + help=('With no argument, print verbose dvsim tool ' + 'messages. With --verbose=debug, the volume of ' + 'messages is even higher.')) + + dvg.add_argument("--dry-run", "-n", + action='store_true', + help=("Print dvsim tool messages but don't actually " + "run any command")) args = parser.parse_args() @@ -447,6 +491,17 @@ def main(): if args.list == []: args.list = _LIST_CATEGORIES + # Get max_parallel from environment if it wasn't specified on the command + # line. + args.max_parallel = resolve_max_parallel(args.max_parallel) + assert args.max_parallel > 0 + + return args + + +def main(): + args = parse_args() + # Add log level 'VERBOSE' between INFO and DEBUG log.addLevelName(utils.VERBOSE, 'VERBOSE') diff --git a/vendor/lowrisc_ip/prim/prim.core b/vendor/lowrisc_ip/prim/prim.core index 166b4cd5..9ab4f550 100644 --- a/vendor/lowrisc_ip/prim/prim.core +++ b/vendor/lowrisc_ip/prim/prim.core @@ -9,8 +9,6 @@ description: "Primitives" filesets: files_rtl: depend: - - lowrisc:prim:ram_2p # for prim_ram_2p_adv - - lowrisc:prim:secded # for prim_ram_2p_adv - lowrisc:prim:assert - lowrisc:prim:diff_decode # for prim_alert_sender/receiver - lowrisc:prim:pad_wrapper @@ -45,8 +43,6 @@ filesets: - rtl/prim_subreg.sv - rtl/prim_subreg_ext.sv - rtl/prim_intr_hw.sv - - rtl/prim_ram_2p_adv.sv - - rtl/prim_ram_2p_async_adv.sv file_type: systemVerilogSource files_verilator_waiver: diff --git a/vendor/lowrisc_ip/prim/prim_flash.core b/vendor/lowrisc_ip/prim/prim_flash.core index 9d7b41ea..c7d1aa3a 100644 --- a/vendor/lowrisc_ip/prim/prim_flash.core +++ b/vendor/lowrisc_ip/prim/prim_flash.core @@ -10,6 +10,9 @@ filesets: depend: - lowrisc:prim:prim_pkg - lowrisc:prim:primgen + # TODO olofk/fusesoc#404: The below dependency is already added to prim_generic_flash.core. + # However, the generator for the prim:ram1p does not kick in, causing compile errors. + - lowrisc:prim:ram_1p generate: impl: diff --git a/vendor/lowrisc_ip/prim/prim_ram_1p_adv.core b/vendor/lowrisc_ip/prim/prim_ram_1p_adv.core index fe655a87..5c3b4199 100644 --- a/vendor/lowrisc_ip/prim/prim_ram_1p_adv.core +++ b/vendor/lowrisc_ip/prim/prim_ram_1p_adv.core @@ -8,6 +8,9 @@ description: "Single-port RAM primitive with advanced features" filesets: files_rtl: depend: + - lowrisc:prim:assert + - lowrisc:prim:util + - lowrisc:prim:secded - lowrisc:prim:ram_1p files: - rtl/prim_ram_1p_adv.sv diff --git a/vendor/lowrisc_ip/prim/prim_ram_2p_adv.core b/vendor/lowrisc_ip/prim/prim_ram_2p_adv.core new file mode 100644 index 00000000..3ff2dcd8 --- /dev/null +++ b/vendor/lowrisc_ip/prim/prim_ram_2p_adv.core @@ -0,0 +1,19 @@ +CAPI=2: +# Copyright lowRISC contributors. +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 + +name: "lowrisc:prim:ram_2p_adv:0.1" +description: "Dual-port RAM primitive with advanced features" +filesets: + files_rtl: + depend: + - lowrisc:prim:ram_2p_async_adv + files: + - rtl/prim_ram_2p_adv.sv + file_type: systemVerilogSource + +targets: + default: + filesets: + - files_rtl diff --git a/vendor/lowrisc_ip/prim/prim_ram_2p_async_adv.core b/vendor/lowrisc_ip/prim/prim_ram_2p_async_adv.core new file mode 100644 index 00000000..e5eda9d0 --- /dev/null +++ b/vendor/lowrisc_ip/prim/prim_ram_2p_async_adv.core @@ -0,0 +1,22 @@ +CAPI=2: +# Copyright lowRISC contributors. +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 + +name: "lowrisc:prim:ram_2p_async_adv:0.1" +description: "Asynchronous dual-port RAM primitive with advanced features" +filesets: + files_rtl: + depend: + - lowrisc:prim:assert + - lowrisc:prim:util + - lowrisc:prim:secded + - lowrisc:prim:ram_2p + files: + - rtl/prim_ram_2p_async_adv.sv + file_type: systemVerilogSource + +targets: + default: + filesets: + - files_rtl diff --git a/vendor/lowrisc_ip/prim/prim_rom_adv.core b/vendor/lowrisc_ip/prim/prim_rom_adv.core new file mode 100644 index 00000000..249d6dd0 --- /dev/null +++ b/vendor/lowrisc_ip/prim/prim_rom_adv.core @@ -0,0 +1,20 @@ +CAPI=2: +# Copyright lowRISC contributors. +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 + +name: "lowrisc:prim:rom_adv:0.1" +description: "ROM primitive with advanced features" +filesets: + files_rtl: + depend: + - lowrisc:prim:assert + - lowrisc:prim:rom + files: + - rtl/prim_rom_adv.sv + file_type: systemVerilogSource + +targets: + default: + filesets: + - files_rtl diff --git a/vendor/lowrisc_ip/prim/prim_util.core b/vendor/lowrisc_ip/prim/prim_util.core new file mode 100644 index 00000000..c46765ba --- /dev/null +++ b/vendor/lowrisc_ip/prim/prim_util.core @@ -0,0 +1,17 @@ +CAPI=2: +# Copyright lowRISC contributors. +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 + +name: "lowrisc:prim:util:0.1" +description: "Utilities" +filesets: + files_rtl: + files: + - rtl/prim_util.svh : {is_include_file : true} + file_type: systemVerilogSource + +targets: + default: + filesets: + - files_rtl diff --git a/vendor/lowrisc_ip/prim/rtl/prim_fifo_sync.sv b/vendor/lowrisc_ip/prim/rtl/prim_fifo_sync.sv index e2199db6..455e63cb 100644 --- a/vendor/lowrisc_ip/prim/rtl/prim_fifo_sync.sv +++ b/vendor/lowrisc_ip/prim/rtl/prim_fifo_sync.sv @@ -10,6 +10,7 @@ module prim_fifo_sync #( parameter int unsigned Width = 16, parameter bit Pass = 1'b1, // if == 1 allow requests to pass through empty FIFO parameter int unsigned Depth = 4, + parameter bit OutputZeroIfEmpty = 1'b1, // if == 1 always output 0 when FIFO is empty // derived parameter localparam int unsigned DepthWNorm = $clog2(Depth+1), localparam int unsigned DepthW = (DepthWNorm == 0) ? 1 : DepthWNorm @@ -131,14 +132,21 @@ module prim_fifo_sync #( end end + logic [Width-1:0] rdata_int; if (Pass == 1'b1) begin : gen_pass - assign rdata = (fifo_empty && wvalid) ? wdata : storage_rdata; + assign rdata_int = (fifo_empty && wvalid) ? wdata : storage_rdata; assign empty = fifo_empty & ~wvalid; end else begin : gen_nopass - assign rdata = storage_rdata; + assign rdata_int = storage_rdata; assign empty = fifo_empty; end + if (OutputZeroIfEmpty == 1'b1) begin : gen_output_zero + assign rdata = empty ? 'b0 : rdata_int; + end else begin : gen_no_output_zero + assign rdata = rdata_int; + end + `ASSERT(depthShallNotExceedParamDepth, !empty |-> depth <= DepthW'(Depth)) end // block: gen_normal_fifo diff --git a/vendor/lowrisc_ip/prim/rtl/prim_lfsr.sv b/vendor/lowrisc_ip/prim/rtl/prim_lfsr.sv index 68481bc5..cf356422 100644 --- a/vendor/lowrisc_ip/prim/rtl/prim_lfsr.sv +++ b/vendor/lowrisc_ip/prim/rtl/prim_lfsr.sv @@ -476,12 +476,12 @@ module prim_lfsr #( perturbed_q <= perturbed_d; end end -`endif `ASSERT(MaximalLengthCheck0_A, cnt_q == 0 |-> lfsr_q == DefaultSeed, clk_i, !rst_ni || perturbed_q) `ASSERT(MaximalLengthCheck1_A, cnt_q != 0 |-> lfsr_q != DefaultSeed, clk_i, !rst_ni || perturbed_q) +`endif end endmodule diff --git a/vendor/lowrisc_ip/prim/rtl/prim_ram_1p_adv.sv b/vendor/lowrisc_ip/prim/rtl/prim_ram_1p_adv.sv index 6d546782..4452a181 100644 --- a/vendor/lowrisc_ip/prim/rtl/prim_ram_1p_adv.sv +++ b/vendor/lowrisc_ip/prim/rtl/prim_ram_1p_adv.sv @@ -3,63 +3,215 @@ // SPDX-License-Identifier: Apache-2.0 // // Single-Port SRAM Wrapper +// +// Supported configurations: +// - ECC for 32b wide memories with no write mask +// (Width == 32 && DataBitsPerMask == 32). +// - Byte parity if Width is a multiple of 8 bit and write masks have Byte +// granularity (DataBitsPerMask == 8). +// +// Note that the write mask needs to be per Byte if parity is enabled. If ECC is enabled, the write +// mask cannot be used and has to be tied to {Width{1'b1}}. `include "prim_assert.sv" +`include "prim_util.svh" module prim_ram_1p_adv #( - // Parameters passed on the the SRAM primitive. - parameter int Width = 32, // bit - parameter int Depth = 128, - parameter int DataBitsPerMask = 1, // Number of data bits per bit of write mask - parameter MemInitFile = "", // VMEM file to initialize the memory with + parameter int Depth = 512, + parameter int Width = 32, + parameter int DataBitsPerMask = 1, // Number of data bits per bit of write mask + parameter int CfgW = 8, // WTC, RTC, etc + parameter MemInitFile = "", // VMEM file to initialize the memory with - parameter int CfgW = 8, // WTC, RTC, etc + // Configurations + parameter bit EnableECC = 0, // Enables per-word ECC + parameter bit EnableParity = 0, // Enables per-Byte Parity + parameter bit EnableInputPipeline = 0, // Adds an input register (read latency +1) + parameter bit EnableOutputPipeline = 0, // Adds an output register (read latency +1) - localparam int Aw = $clog2(Depth) + localparam int Aw = vbits(Depth) ) ( - input clk_i, - input rst_ni, + input clk_i, + input rst_ni, - input req_i, - input write_i, - input [Aw-1:0] addr_i, - input [Width-1:0] wdata_i, - input [Width-1:0] wmask_i, - output logic [Width-1:0] rdata_o, - output logic rvalid_o, // read response (rdata_o) is valid - output logic [1:0] rerror_o, // Bit1: Uncorrectable, Bit0: Correctable + input req_i, + input write_i, + input [Aw-1:0] addr_i, + input [Width-1:0] wdata_i, + input [Width-1:0] wmask_i, + output logic [Width-1:0] rdata_o, + output logic rvalid_o, // read response (rdata_o) is valid + output logic [1:0] rerror_o, // Bit1: Uncorrectable, Bit0: Correctable - input [CfgW-1:0] cfg_i + // config + input [CfgW-1:0] cfg_i ); - // We will eventually use cfg_i for RTC/WTC or other memory parameters. - logic [CfgW-1:0] unused_cfg; - assign unused_cfg = cfg_i; + `ASSERT_INIT(CannotHaveEccAndParity_A, !(EnableParity && EnableECC)) + + // While we require DataBitsPerMask to be per Byte (8) at the interface in case Byte parity is + // enabled, we need to switch this to a per-bit mask locally such that we can individually enable + // the parity bits to be written alongside the data. + localparam int LocalDataBitsPerMask = (EnableParity) ? 1 : DataBitsPerMask; + + // Calculate ECC width + localparam int ParWidth = (EnableParity) ? Width/8 : + (!EnableECC) ? 0 : + (Width <= 4) ? 4 : + (Width <= 11) ? 5 : + (Width <= 26) ? 6 : + (Width <= 57) ? 7 : + (Width <= 120) ? 8 : 8 ; + localparam int TotalWidth = Width + ParWidth; + + //////////////////////////// + // RAM Primitive Instance // + //////////////////////////// + + logic req_q, req_d ; + logic write_q, write_d ; + logic [Aw-1:0] addr_q, addr_d ; + logic [TotalWidth-1:0] wdata_q, wdata_d ; + logic [TotalWidth-1:0] wmask_q, wmask_d ; + logic rvalid_q, rvalid_d, rvalid_sram ; + logic [Width-1:0] rdata_q, rdata_d ; + logic [TotalWidth-1:0] rdata_sram ; + logic [1:0] rerror_q, rerror_d ; prim_ram_1p #( - .Width (Width), + .MemInitFile (MemInitFile), + + .Width (TotalWidth), .Depth (Depth), - .DataBitsPerMask (DataBitsPerMask), - .MemInitFile (MemInitFile) + .DataBitsPerMask (LocalDataBitsPerMask) ) u_mem ( .clk_i, - .req_i, - .write_i, - .addr_i, - .wdata_i, - .wmask_i, - .rdata_o + .req_i (req_q), + .write_i (write_q), + .addr_i (addr_q), + .wdata_i (wdata_q), + .wmask_i (wmask_q), + .rdata_o (rdata_sram) ); - always_ff @(posedge clk_i, negedge rst_ni) begin + always_ff @(posedge clk_i or negedge rst_ni) begin if (!rst_ni) begin - rvalid_o <= '0; + rvalid_sram <= 1'b0; end else begin - rvalid_o <= req_i & ~write_i; + rvalid_sram <= req_q & ~write_q; end end - assign rerror_o = 2'b0; + assign req_d = req_i; + assign write_d = write_i; + assign addr_d = addr_i; + assign rvalid_o = rvalid_q; + assign rdata_o = rdata_q; + assign rerror_o = rerror_q; -endmodule + ///////////////////////////// + // ECC / Parity Generation // + ///////////////////////////// + + if (EnableParity == 0 && EnableECC) begin : gen_secded + + // check supported widths + `ASSERT_INIT(SecDecWidth_A, Width inside {32}) + + // the wmask is constantly set to 1 in this case + `ASSERT(OnlyWordWritePossibleWithEccPortA_A, req_i |-> + wmask_i == {TotalWidth{1'b1}}) + + assign wmask_d = {TotalWidth{1'b1}}; + + if (Width == 32) begin : gen_secded_39_32 + prim_secded_39_32_enc u_enc (.in(wdata_i), .out(wdata_d)); + prim_secded_39_32_dec u_dec ( + .in (rdata_sram), + .d_o (rdata_d[0+:Width]), + .syndrome_o ( ), + .err_o (rerror_d) + ); + end + end else if (EnableParity) begin : gen_byte_parity + + `ASSERT_INIT(WidthNeedsToBeByteAligned_A, Width % 8 == 0) + `ASSERT_INIT(ParityNeedsByteWriteMask_A, DataBitsPerMask == 8) + + always_comb begin : p_parity + rerror_d = '0; + wmask_d[0+:Width] = wmask_i; + wdata_d[0+:Width] = wdata_i; + + for (int i = 0; i < Width/8; i ++) begin + // parity generation (odd parity) + wdata_d[Width + i] = ~(^wdata_i[i*8 +: 8]); + wmask_d[Width + i] = &wmask_i[i*8 +: 8]; + // parity decoding (errors are always uncorrectable) + rerror_d[1] |= ~(^{rdata_sram[i*8 +: 8], rdata_sram[Width + i]}); + end + // tie to zero if the read data is not valid + rerror_d &= {2{rvalid_sram}}; + end + + assign rdata_d = rdata_sram[0+:Width]; + end else begin : gen_nosecded_noparity + assign wmask_d = wmask_i; + assign wdata_d = wdata_i; + + assign rdata_d = rdata_sram[0+:Width]; + assign rerror_d = '0; + end + + assign rvalid_d = rvalid_sram; + + ///////////////////////////////////// + // Input/Output Pipeline Registers // + ///////////////////////////////////// + + if (EnableInputPipeline) begin : gen_regslice_input + // Put the register slices between ECC encoding to SRAM port + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + req_q <= '0; + write_q <= '0; + addr_q <= '0; + wdata_q <= '0; + wmask_q <= '0; + end else begin + req_q <= req_d; + write_q <= write_d; + addr_q <= addr_d; + wdata_q <= wdata_d; + wmask_q <= wmask_d; + end + end + end else begin : gen_dirconnect_input + assign req_q = req_d; + assign write_q = write_d; + assign addr_q = addr_d; + assign wdata_q = wdata_d; + assign wmask_q = wmask_d; + end + + if (EnableOutputPipeline) begin : gen_regslice_output + // Put the register slices between ECC decoding to output + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + rvalid_q <= '0; + rdata_q <= '0; + rerror_q <= '0; + end else begin + rvalid_q <= rvalid_d; + rdata_q <= rdata_d; + rerror_q <= rerror_d; + end + end + end else begin : gen_dirconnect_output + assign rvalid_q = rvalid_d; + assign rdata_q = rdata_d; + assign rerror_q = rerror_d; + end + +endmodule : prim_ram_1p_adv diff --git a/vendor/lowrisc_ip/prim/rtl/prim_ram_2p_adv.sv b/vendor/lowrisc_ip/prim/rtl/prim_ram_2p_adv.sv index 4fc1b729..2d9e5a80 100644 --- a/vendor/lowrisc_ip/prim/rtl/prim_ram_2p_adv.sv +++ b/vendor/lowrisc_ip/prim/rtl/prim_ram_2p_adv.sv @@ -2,271 +2,91 @@ // Licensed under the Apache License, Version 2.0, see LICENSE for details. // SPDX-License-Identifier: Apache-2.0 // -// Dual-port SRAM Wrapper -// This module to connect SRAM interface to actual SRAM interface -// At this time, it doesn't utilize ECC or any pipeline. -// This module stays to include any additional calculation logic later on. -// Instantiating SRAM is up to the top design to remove process dependency. - -// Parameter -// EnableECC: -// EnableParity: -// EnableInputPipeline: -// EnableOutputPipeline: +// Dual-Port SRAM Wrapper +// +// Supported configurations: +// - ECC for 32b wide memories with no write mask +// (Width == 32 && DataBitsPerMask == 32). +// - Byte parity if Width is a multiple of 8 bit and write masks have Byte +// granularity (DataBitsPerMask == 8). +// +// Note that the write mask needs to be per Byte if parity is enabled. If ECC is enabled, the write +// mask cannot be used and has to be tied to {Width{1'b1}}. `include "prim_assert.sv" +`include "prim_util.svh" module prim_ram_2p_adv #( parameter int Depth = 512, parameter int Width = 32, - parameter int CfgW = 8, // WTC, RTC, etc + parameter int DataBitsPerMask = 1, // Number of data bits per bit of write mask + parameter int CfgW = 8, // WTC, RTC, etc parameter MemInitFile = "", // VMEM file to initialize the memory with // Configurations - parameter bit EnableECC = 0, - parameter bit EnableParity = 0, - parameter bit EnableInputPipeline = 0, - parameter bit EnableOutputPipeline = 0, + parameter bit EnableECC = 0, // Enables per-word ECC + parameter bit EnableParity = 0, // Enables per-Byte Parity + parameter bit EnableInputPipeline = 0, // Adds an input register (read latency +1) + parameter bit EnableOutputPipeline = 0, // Adds an output register (read latency +1) - parameter MemT = "REGISTER", // can be "REGISTER" or "SRAM" - - localparam int Aw = $clog2(Depth) + localparam int Aw = vbits(Depth) ) ( - input clk_i, - input rst_ni, + input clk_i, + input rst_ni, - input a_req_i, - input a_write_i, - input [Aw-1:0] a_addr_i, - input [Width-1:0] a_wdata_i, - output logic [Width-1:0] a_rdata_o, - output logic a_rvalid_o, // read response (a_rdata_o) is valid - output logic [1:0] a_rerror_o, // Bit1: Uncorrectable, Bit0: Correctable + input a_req_i, + input a_write_i, + input [Aw-1:0] a_addr_i, + input [Width-1:0] a_wdata_i, + input [Width-1:0] a_wmask_i, // cannot be used with ECC, tie to 1 in that case + output logic [Width-1:0] a_rdata_o, + output logic a_rvalid_o, // read response (a_rdata_o) is valid + output logic [1:0] a_rerror_o, // Bit1: Uncorrectable, Bit0: Correctable - input b_req_i, - input b_write_i, - input [Aw-1:0] b_addr_i, - input [Width-1:0] b_wdata_i, - output logic [Width-1:0] b_rdata_o, - output logic b_rvalid_o, // read response (b_rdata_o) is valid - output logic [1:0] b_rerror_o, // Bit1: Uncorrectable, Bit0: Correctable + input b_req_i, + input b_write_i, + input [Aw-1:0] b_addr_i, + input [Width-1:0] b_wdata_i, + input [Width-1:0] b_wmask_i, // cannot be used with ECC, tie to 1 in that case + output logic [Width-1:0] b_rdata_o, + output logic b_rvalid_o, // read response (b_rdata_o) is valid + output logic [1:0] b_rerror_o, // Bit1: Uncorrectable, Bit0: Correctable - input [CfgW-1:0] cfg_i + input [CfgW-1:0] cfg_i ); - // Calculate ECC width - localparam int ParWidth = (EnableParity) ? 1 : - (!EnableECC) ? 0 : - (Width <= 4) ? 4 : - (Width <= 11) ? 5 : - (Width <= 26) ? 6 : - (Width <= 57) ? 7 : - (Width <= 120) ? 8 : 8 ; - localparam int TotalWidth = Width + ParWidth; + prim_ram_2p_async_adv #( + .Depth (Depth), + .Width (Width), + .DataBitsPerMask (DataBitsPerMask), + .CfgW (CfgW), + .MemInitFile (MemInitFile), + .EnableECC (EnableECC), + .EnableParity (EnableParity), + .EnableInputPipeline (EnableInputPipeline), + .EnableOutputPipeline(EnableOutputPipeline) + ) i_prim_ram_2p_async_adv ( + .clk_a_i(clk_i), + .rst_a_ni(rst_ni), + .clk_b_i(clk_i), + .rst_b_ni(rst_ni), + .a_req_i, + .a_write_i, + .a_addr_i, + .a_wdata_i, + .a_wmask_i, + .a_rdata_o, + .a_rvalid_o, + .a_rerror_o, + .b_req_i, + .b_write_i, + .b_addr_i, + .b_wdata_i, + .b_wmask_i, + .b_rdata_o, + .b_rvalid_o, + .b_rerror_o, + .cfg_i + ); - // We will eventually use cfg_i for RTC/WTC or other memory parameters. - logic [CfgW-1:0] unused_cfg; - assign unused_cfg = cfg_i; - - logic a_req_q, a_req_d ; - logic a_write_q, a_write_d ; - logic [Aw-1:0] a_addr_q, a_addr_d ; - logic [TotalWidth-1:0] a_wdata_q, a_wdata_d ; - logic a_rvalid_q, a_rvalid_d, a_rvalid_sram ; - logic [Width-1:0] a_rdata_q, a_rdata_d ; - logic [TotalWidth-1:0] a_rdata_sram ; - logic [1:0] a_rerror_q, a_rerror_d ; - - logic b_req_q, b_req_d ; - logic b_write_q, b_write_d ; - logic [Aw-1:0] b_addr_q, b_addr_d ; - logic [TotalWidth-1:0] b_wdata_q, b_wdata_d ; - logic b_rvalid_q, b_rvalid_d, b_rvalid_sram ; - logic [Width-1:0] b_rdata_q, b_rdata_d ; - logic [TotalWidth-1:0] b_rdata_sram ; - logic [1:0] b_rerror_q, b_rerror_d ; - - if (MemT == "REGISTER") begin : gen_regmem - prim_ram_2p #( - // force register implementation for all targets - .Impl (prim_pkg::ImplGeneric), - - .Width (TotalWidth), - .Depth (Depth), - .DataBitsPerMask (TotalWidth), - .MemInitFile (MemInitFile) - ) u_mem ( - .clk_a_i (clk_i), - .clk_b_i (clk_i), - - .a_req_i (a_req_q), - .a_write_i (a_write_q), - .a_addr_i (a_addr_q), - .a_wdata_i (a_wdata_q), - .a_wmask_i ({TotalWidth{1'b1}}), - .a_rdata_o (a_rdata_sram), - - .b_req_i (b_req_q), - .b_write_i (b_write_q), - .b_addr_i (b_addr_q), - .b_wdata_i (b_wdata_q), - .b_wmask_i ({TotalWidth{1'b1}}), - .b_rdata_o (b_rdata_sram) - ); - // end else if (TotalWidth == aa && Depth == yy) begin - end else if (MemT == "SRAM") begin : gen_srammem - prim_ram_2p #( - .Width (TotalWidth), - .Depth (Depth), - .DataBitsPerMask (TotalWidth), - .MemInitFile (MemInitFile) - ) u_mem ( - .clk_a_i (clk_i), - .clk_b_i (clk_i), - - .a_req_i (a_req_q), - .a_write_i (a_write_q), - .a_addr_i (a_addr_q), - .a_wdata_i (a_wdata_q), - .a_wmask_i ({TotalWidth{1'b1}}), - .a_rdata_o (a_rdata_sram), - - .b_req_i (b_req_q), - .b_write_i (b_write_q), - .b_addr_i (b_addr_q), - .b_wdata_i (b_wdata_q), - .b_wmask_i ({TotalWidth{1'b1}}), - .b_rdata_o (b_rdata_sram) - ); - end - - always_ff @(posedge clk_i, negedge rst_ni) begin - if (!rst_ni) begin - a_rvalid_sram <= '0; - b_rvalid_sram <= '0; - end else begin - a_rvalid_sram <= a_req_q & ~a_write_q; - b_rvalid_sram <= b_req_q & ~b_write_q; - end - end - - assign a_req_d = a_req_i; - assign a_write_d = a_write_i; - assign a_addr_d = a_addr_i; - assign a_rvalid_o = a_rvalid_q; - assign a_rdata_o = a_rdata_q; - assign a_rerror_o = a_rerror_q; - - assign b_req_d = b_req_i; - assign b_write_d = b_write_i; - assign b_addr_d = b_addr_i; - assign b_rvalid_o = b_rvalid_q; - assign b_rdata_o = b_rdata_q; - assign b_rerror_o = b_rerror_q; - - // TODO: Parity Logic - `ASSERT_INIT(ParityNotYetSupported_A, EnableParity == 0) - - if (EnableParity == 0 && EnableECC) begin : gen_secded - - // check supported widths - `ASSERT_INIT(SecDecWidth_A, Width inside {32}) - - if (Width == 32) begin : gen_secded_39_32 - prim_secded_39_32_enc u_enc_a (.in(a_wdata_i), .out(a_wdata_d)); - prim_secded_39_32_dec u_dec_a ( - .in (a_rdata_sram), - .d_o (a_rdata_d), - .syndrome_o (), - .err_o (a_rerror_d) - ); - prim_secded_39_32_enc u_enc_b (.in(b_wdata_i), .out(b_wdata_d)); - prim_secded_39_32_dec u_dec_b ( - .in (b_rdata_sram), - .d_o (b_rdata_d), - .syndrome_o (), - .err_o (b_rerror_d) - ); - assign a_rvalid_d = a_rvalid_sram; - assign b_rvalid_d = b_rvalid_sram; - end - end else begin : gen_nosecded - assign a_wdata_d[0+:Width] = a_wdata_i; - assign b_wdata_d[0+:Width] = b_wdata_i; - assign a_rdata_d = a_rdata_sram; - assign b_rdata_d = b_rdata_sram; - assign a_rvalid_d = a_rvalid_sram; - assign b_rvalid_d = b_rvalid_sram; - assign a_rerror_d = 2'b00; - assign b_rerror_d = 2'b00; - end - - if (EnableInputPipeline) begin : gen_regslice_input - // Put the register slices between ECC encoding to SRAM port - always_ff @(posedge clk_i or negedge rst_ni) begin - if (!rst_ni) begin - a_req_q <= '0; - a_write_q <= '0; - a_addr_q <= '0; - a_wdata_q <= '0; - - b_req_q <= '0; - b_write_q <= '0; - b_addr_q <= '0; - b_wdata_q <= '0; - end else begin - a_req_q <= a_req_d; - a_write_q <= a_write_d; - a_addr_q <= a_addr_d; - a_wdata_q <= a_wdata_d; - - b_req_q <= b_req_d; - b_write_q <= b_write_d; - b_addr_q <= b_addr_d; - b_wdata_q <= b_wdata_d; - end - end - end else begin : gen_dirconnect_input - assign a_req_q = a_req_d; - assign a_write_q = a_write_d; - assign a_addr_q = a_addr_d; - assign a_wdata_q = a_wdata_d; - - assign b_req_q = b_req_d; - assign b_write_q = b_write_d; - assign b_addr_q = b_addr_d; - assign b_wdata_q = b_wdata_d; - end - - if (EnableOutputPipeline) begin : gen_regslice_output - // Put the register slices between ECC decoding to output - always_ff @(posedge clk_i or negedge rst_ni) begin - if (!rst_ni) begin - a_rvalid_q <= '0; - a_rdata_q <= '0; - a_rerror_q <= '0; - - b_rvalid_q <= '0; - b_rdata_q <= '0; - b_rerror_q <= '0; - end else begin - a_rvalid_q <= a_rvalid_d; - a_rdata_q <= a_rdata_d ; - a_rerror_q <= a_rerror_d; - - b_rvalid_q <= b_rvalid_d; - b_rdata_q <= b_rdata_d ; - b_rerror_q <= b_rerror_d; - end - end - end else begin : gen_dirconnect_output - assign a_rvalid_q = a_rvalid_d; - assign a_rdata_q = a_rdata_d; - assign a_rerror_q = a_rerror_d; - - assign b_rvalid_q = b_rvalid_d; - assign b_rdata_q = b_rdata_d; - assign b_rerror_q = b_rerror_d; - end - -endmodule +endmodule : prim_ram_2p_adv diff --git a/vendor/lowrisc_ip/prim/rtl/prim_ram_2p_async_adv.sv b/vendor/lowrisc_ip/prim/rtl/prim_ram_2p_async_adv.sv index a1c8474b..ab0cd08d 100644 --- a/vendor/lowrisc_ip/prim/rtl/prim_ram_2p_async_adv.sv +++ b/vendor/lowrisc_ip/prim/rtl/prim_ram_2p_async_adv.sv @@ -2,63 +2,71 @@ // Licensed under the Apache License, Version 2.0, see LICENSE for details. // SPDX-License-Identifier: Apache-2.0 // -// Dual-port SRAM Wrapper -// This module to connect SRAM interface to actual SRAM interface -// At this time, it doesn't utilize ECC or any pipeline. -// This module stays to include any additional calculation logic later on. -// Instantiating SRAM is up to the top design to remove process dependency. - -// Parameter -// EnableECC: -// EnableParity: -// EnableInputPipeline: -// EnableOutputPipeline: +// Asynchronous Dual-Port SRAM Wrapper +// +// Supported configurations: +// - ECC for 32b wide memories with no write mask +// (Width == 32 && DataBitsPerMask == 32). +// - Byte parity if Width is a multiple of 8 bit and write masks have Byte +// granularity (DataBitsPerMask == 8). +// +// Note that the write mask needs to be per Byte if parity is enabled. If ECC is enabled, the write +// mask cannot be used and has to be tied to {Width{1'b1}}. `include "prim_assert.sv" +`include "prim_util.svh" module prim_ram_2p_async_adv #( - parameter int Depth = 512, - parameter int Width = 32, - parameter int CfgW = 8, // WTC, RTC, etc + parameter int Depth = 512, + parameter int Width = 32, + parameter int DataBitsPerMask = 1, // Number of data bits per bit of write mask + parameter int CfgW = 8, // WTC, RTC, etc + parameter MemInitFile = "", // VMEM file to initialize the memory with // Configurations - parameter bit EnableECC = 0, - parameter bit EnableParity = 0, - parameter bit EnableInputPipeline = 0, - parameter bit EnableOutputPipeline = 0, + parameter bit EnableECC = 0, // Enables per-word ECC + parameter bit EnableParity = 0, // Enables per-Byte Parity + parameter bit EnableInputPipeline = 0, // Adds an input register (read latency +1) + parameter bit EnableOutputPipeline = 0, // Adds an output register (read latency +1) - parameter MemT = "REGISTER", // can be "REGISTER" or "SRAM" - - // Do not touch - parameter int SramAw = $clog2(Depth) + localparam int Aw = vbits(Depth) ) ( input clk_a_i, input clk_b_i, input rst_a_ni, input rst_b_ni, - input a_req_i, - input a_write_i, - input [SramAw-1:0] a_addr_i, - input [Width-1:0] a_wdata_i, - output logic a_rvalid_o, - output logic [Width-1:0] a_rdata_o, - output logic [1:0] a_rerror_o, + input a_req_i, + input a_write_i, + input [Aw-1:0] a_addr_i, + input [Width-1:0] a_wdata_i, + input [Width-1:0] a_wmask_i, // cannot be used with ECC, tie to 1 in that case + output logic [Width-1:0] a_rdata_o, + output logic a_rvalid_o, // read response (a_rdata_o) is valid + output logic [1:0] a_rerror_o, // Bit1: Uncorrectable, Bit0: Correctable - input b_req_i, - input b_write_i, - input [SramAw-1:0] b_addr_i, - input [Width-1:0] b_wdata_i, - output logic b_rvalid_o, - output logic [Width-1:0] b_rdata_o, - output logic [1:0] b_rerror_o, // Bit1: Uncorrectable, Bit0: Correctable + input b_req_i, + input b_write_i, + input [Aw-1:0] b_addr_i, + input [Width-1:0] b_wdata_i, + input [Width-1:0] b_wmask_i, // cannot be used with ECC, tie to 1 in that case + output logic [Width-1:0] b_rdata_o, + output logic b_rvalid_o, // read response (b_rdata_o) is valid + output logic [1:0] b_rerror_o, // Bit1: Uncorrectable, Bit0: Correctable // config input [CfgW-1:0] cfg_i ); + `ASSERT_INIT(CannotHaveEccAndParity_A, !(EnableParity && EnableECC)) + + // While we require DataBitsPerMask to be per Byte (8) at the interface in case Byte parity is + // enabled, we need to switch this to a per-bit mask locally such that we can individually enable + // the parity bits to be written alongside the data. + localparam int LocalDataBitsPerMask = (EnableParity) ? 1 : DataBitsPerMask; + // Calculate ECC width - localparam int ParWidth = (EnableParity) ? 1 : + localparam int ParWidth = (EnableParity) ? Width/8 : (!EnableECC) ? 0 : (Width <= 4) ? 4 : (Width <= 11) ? 5 : @@ -67,75 +75,54 @@ module prim_ram_2p_async_adv #( (Width <= 120) ? 8 : 8 ; localparam int TotalWidth = Width + ParWidth; - logic a_req_q, a_req_d ; - logic a_write_q, a_write_d ; - logic [SramAw-1:0] a_addr_q, a_addr_d ; - logic [TotalWidth-1:0] a_wdata_q, a_wdata_d ; - logic a_rvalid_q, a_rvalid_d, a_rvalid_sram ; - logic [TotalWidth-1:0] a_rdata_d, a_rdata_sram ; - logic [Width-1:0] a_rdata_q ; - logic [1:0] a_rerror_q, a_rerror_d ; + //////////////////////////// + // RAM Primitive Instance // + //////////////////////////// - logic b_req_q, b_req_d ; - logic b_write_q, b_write_d ; - logic [SramAw-1:0] b_addr_q, b_addr_d ; - logic [TotalWidth-1:0] b_wdata_q, b_wdata_d ; - logic b_rvalid_q, b_rvalid_d, b_rvalid_sram ; - logic [TotalWidth-1:0] b_rdata_d, b_rdata_sram ; - logic [Width-1:0] b_rdata_q ; - logic [1:0] b_rerror_q, b_rerror_d ; + logic a_req_q, a_req_d ; + logic a_write_q, a_write_d ; + logic [Aw-1:0] a_addr_q, a_addr_d ; + logic [TotalWidth-1:0] a_wdata_q, a_wdata_d ; + logic [TotalWidth-1:0] a_wmask_q, a_wmask_d ; + logic a_rvalid_q, a_rvalid_d, a_rvalid_sram ; + logic [Width-1:0] a_rdata_q, a_rdata_d ; + logic [TotalWidth-1:0] a_rdata_sram ; + logic [1:0] a_rerror_q, a_rerror_d ; - if (MemT == "REGISTER") begin : gen_regmem - prim_ram_2p #( - // force register implementation for all targets - .Impl (prim_pkg::ImplGeneric), + logic b_req_q, b_req_d ; + logic b_write_q, b_write_d ; + logic [Aw-1:0] b_addr_q, b_addr_d ; + logic [TotalWidth-1:0] b_wdata_q, b_wdata_d ; + logic [TotalWidth-1:0] b_wmask_q, b_wmask_d ; + logic b_rvalid_q, b_rvalid_d, b_rvalid_sram ; + logic [Width-1:0] b_rdata_q, b_rdata_d ; + logic [TotalWidth-1:0] b_rdata_sram ; + logic [1:0] b_rerror_q, b_rerror_d ; - .Width (TotalWidth), - .Depth (Depth), - .DataBitsPerMask (TotalWidth) - ) u_mem ( - .clk_a_i (clk_a_i), - .clk_b_i (clk_b_i), + prim_ram_2p #( + .MemInitFile (MemInitFile), - .a_req_i (a_req_q), - .a_write_i (a_write_q), - .a_addr_i (a_addr_q), - .a_wdata_i (a_wdata_q), - .a_wmask_i ({TotalWidth{1'b1}}), - .a_rdata_o (a_rdata_sram), + .Width (TotalWidth), + .Depth (Depth), + .DataBitsPerMask (LocalDataBitsPerMask) + ) u_mem ( + .clk_a_i (clk_a_i), + .clk_b_i (clk_b_i), - .b_req_i (b_req_q), - .b_write_i (b_write_q), - .b_addr_i (b_addr_q), - .b_wdata_i (b_wdata_q), - .b_wmask_i ({TotalWidth{1'b1}}), - .b_rdata_o (b_rdata_sram) - ); - // end else if (TotalWidth == aa && Depth == yy) begin - end else if (MemT == "SRAM") begin : gen_srammem - prim_ram_2p #( - .Width (TotalWidth), - .Depth (Depth), - .DataBitsPerMask (TotalWidth) - ) u_mem ( - .clk_a_i (clk_a_i), - .clk_b_i (clk_b_i), + .a_req_i (a_req_q), + .a_write_i (a_write_q), + .a_addr_i (a_addr_q), + .a_wdata_i (a_wdata_q), + .a_wmask_i (a_wmask_q), + .a_rdata_o (a_rdata_sram), - .a_req_i (a_req_q), - .a_write_i (a_write_q), - .a_addr_i (a_addr_q), - .a_wdata_i (a_wdata_q), - .a_wmask_i ({TotalWidth{1'b1}}), - .a_rdata_o (a_rdata_sram), - - .b_req_i (b_req_q), - .b_write_i (b_write_q), - .b_addr_i (b_addr_q), - .b_wdata_i (b_wdata_q), - .b_wmask_i ({TotalWidth{1'b1}}), - .b_rdata_o (b_rdata_sram) - ); - end + .b_req_i (b_req_q), + .b_write_i (b_write_q), + .b_addr_i (b_addr_q), + .b_wdata_i (b_wdata_q), + .b_wmask_i (b_wmask_q), + .b_rdata_o (b_rdata_sram) + ); always_ff @(posedge clk_a_i or negedge rst_a_ni) begin if (!rst_a_ni) begin @@ -166,43 +153,88 @@ module prim_ram_2p_async_adv #( assign b_rdata_o = b_rdata_q; assign b_rerror_o = b_rerror_q; - // TODO: Parity Logic - `ASSERT_INIT(ParityNotYetSupported_A, EnableParity == 0) + ///////////////////////////// + // ECC / Parity Generation // + ///////////////////////////// if (EnableParity == 0 && EnableECC) begin : gen_secded // check supported widths `ASSERT_INIT(SecDecWidth_A, Width inside {32}) + // the wmask is constantly set to 1 in this case + `ASSERT(OnlyWordWritePossibleWithEccPortA_A, a_req_i |-> + a_wmask_i == {TotalWidth{1'b1}}, clk_a_i, rst_a_ni) + `ASSERT(OnlyWordWritePossibleWithEccPortB_A, b_req_i |-> + b_wmask_i == {TotalWidth{1'b1}}, clk_b_i, rst_b_ni) + + assign a_wmask_d = {TotalWidth{1'b1}}; + assign b_wmask_d = {TotalWidth{1'b1}}; + if (Width == 32) begin : gen_secded_39_32 prim_secded_39_32_enc u_enc_a (.in(a_wdata_i), .out(a_wdata_d)); prim_secded_39_32_dec u_dec_a ( .in (a_rdata_sram), .d_o (a_rdata_d[0+:Width]), - .syndrome_o (a_rdata_d[Width+:ParWidth]), + .syndrome_o ( ), .err_o (a_rerror_d) ); prim_secded_39_32_enc u_enc_b (.in(b_wdata_i), .out(b_wdata_d)); prim_secded_39_32_dec u_dec_b ( .in (b_rdata_sram), .d_o (b_rdata_d[0+:Width]), - .syndrome_o (b_rdata_d[Width+:ParWidth]), + .syndrome_o ( ), .err_o (b_rerror_d) ); - assign a_rvalid_d = a_rvalid_sram; - assign b_rvalid_d = b_rvalid_sram; end - end else begin : gen_nosecded - assign a_wdata_d[0+:Width] = a_wdata_i; - assign b_wdata_d[0+:Width] = b_wdata_i; - assign a_rdata_d[0+:Width] = a_rdata_sram; - assign b_rdata_d[0+:Width] = b_rdata_sram; - assign a_rvalid_d = a_rvalid_sram; - assign b_rvalid_d = b_rvalid_sram; - assign a_rerror_d = 2'b00; - assign b_rerror_d = 2'b00; + end else if (EnableParity) begin : gen_byte_parity + + `ASSERT_INIT(ParityNeedsByteWriteMask_A, DataBitsPerMask == 8) + `ASSERT_INIT(WidthNeedsToBeByteAligned_A, Width % 8 == 0) + + always_comb begin : p_parity + a_rerror_d = '0; + b_rerror_d = '0; + a_wmask_d[0+:Width] = a_wmask_i; + b_wmask_d[0+:Width] = b_wmask_i; + a_wdata_d[0+:Width] = a_wdata_i; + b_wdata_d[0+:Width] = b_wdata_i; + + for (int i = 0; i < Width/8; i ++) begin + // parity generation (odd parity) + a_wdata_d[Width + i] = ~(^a_wdata_i[i*8 +: 8]); + b_wdata_d[Width + i] = ~(^b_wdata_i[i*8 +: 8]); + a_wmask_d[Width + i] = &a_wmask_i[i*8 +: 8]; + b_wmask_d[Width + i] = &b_wmask_i[i*8 +: 8]; + // parity decoding (errors are always uncorrectable) + a_rerror_d[1] |= ~(^{a_rdata_sram[i*8 +: 8], a_rdata_sram[Width + i]}); + b_rerror_d[1] |= ~(^{b_rdata_sram[i*8 +: 8], b_rdata_sram[Width + i]}); + end + // tie to zero if the read data is not valid + a_rerror_d &= {2{a_rvalid_sram}}; + b_rerror_d &= {2{b_rvalid_sram}}; + end + + assign a_rdata_d = a_rdata_sram[0+:Width]; + assign b_rdata_d = b_rdata_sram[0+:Width]; + end else begin : gen_nosecded_noparity + assign a_wmask_d = a_wmask_i; + assign b_wmask_d = b_wmask_i; + assign a_wdata_d = a_wdata_i; + assign b_wdata_d = b_wdata_i; + assign a_rdata_d = a_rdata_sram[0+:Width]; + assign b_rdata_d = b_rdata_sram[0+:Width]; + assign a_rerror_d = '0; + assign b_rerror_d = '0; end + assign a_rvalid_d = a_rvalid_sram; + assign b_rvalid_d = b_rvalid_sram; + + ///////////////////////////////////// + // Input/Output Pipeline Registers // + ///////////////////////////////////// + if (EnableInputPipeline) begin : gen_regslice_input // Put the register slices between ECC encoding to SRAM port always_ff @(posedge clk_a_i or negedge rst_a_ni) begin @@ -211,11 +243,13 @@ module prim_ram_2p_async_adv #( a_write_q <= '0; a_addr_q <= '0; a_wdata_q <= '0; + a_wmask_q <= '0; end else begin a_req_q <= a_req_d; a_write_q <= a_write_d; a_addr_q <= a_addr_d; a_wdata_q <= a_wdata_d; + a_wmask_q <= a_wmask_d; end end always_ff @(posedge clk_b_i or negedge rst_b_ni) begin @@ -224,11 +258,13 @@ module prim_ram_2p_async_adv #( b_write_q <= '0; b_addr_q <= '0; b_wdata_q <= '0; + b_wmask_q <= '0; end else begin b_req_q <= b_req_d; b_write_q <= b_write_d; b_addr_q <= b_addr_d; b_wdata_q <= b_wdata_d; + b_wmask_q <= b_wmask_d; end end end else begin : gen_dirconnect_input @@ -236,11 +272,13 @@ module prim_ram_2p_async_adv #( assign a_write_q = a_write_d; assign a_addr_q = a_addr_d; assign a_wdata_q = a_wdata_d; + assign a_wmask_q = a_wmask_d; assign b_req_q = b_req_d; assign b_write_q = b_write_d; assign b_addr_q = b_addr_d; assign b_wdata_q = b_wdata_d; + assign b_wmask_q = b_wmask_d; end if (EnableOutputPipeline) begin : gen_regslice_output @@ -252,7 +290,7 @@ module prim_ram_2p_async_adv #( a_rerror_q <= '0; end else begin a_rvalid_q <= a_rvalid_d; - a_rdata_q <= a_rdata_d[0+:Width] ; + a_rdata_q <= a_rdata_d; a_rerror_q <= a_rerror_d; end end @@ -263,18 +301,18 @@ module prim_ram_2p_async_adv #( b_rerror_q <= '0; end else begin b_rvalid_q <= b_rvalid_d; - b_rdata_q <= b_rdata_d[0+:Width] ; + b_rdata_q <= b_rdata_d; b_rerror_q <= b_rerror_d; end end end else begin : gen_dirconnect_output assign a_rvalid_q = a_rvalid_d; - assign a_rdata_q = a_rdata_d[0+:Width]; + assign a_rdata_q = a_rdata_d; assign a_rerror_q = a_rerror_d; assign b_rvalid_q = b_rvalid_d; - assign b_rdata_q = b_rdata_d[0+:Width]; + assign b_rdata_q = b_rdata_d; assign b_rerror_q = b_rerror_d; end -endmodule +endmodule : prim_ram_2p_async_adv diff --git a/vendor/lowrisc_ip/prim/rtl/prim_rom_adv.sv b/vendor/lowrisc_ip/prim/rtl/prim_rom_adv.sv new file mode 100644 index 00000000..b0c26ce1 --- /dev/null +++ b/vendor/lowrisc_ip/prim/rtl/prim_rom_adv.sv @@ -0,0 +1,58 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// ROM wrapper with rvalid register + +`include "prim_assert.sv" + +module prim_rom_adv #( + // Parameters passed on the the ROM primitive. + parameter int Width = 32, + parameter int Depth = 2048, // 8kB default + parameter MemInitFile = "", // VMEM file to initialize the memory with + + parameter int CfgW = 8, // WTC, RTC, etc + + localparam int Aw = $clog2(Depth) +) ( + input logic clk_i, + input logic rst_ni, + input logic req_i, + input logic [Aw-1:0] addr_i, + output logic rvalid_o, + output logic [Width-1:0] rdata_o, + + input [CfgW-1:0] cfg_i +); + + // We will eventually use cfg_i for RTC/WTC or other memory parameters. + logic [CfgW-1:0] unused_cfg; + assign unused_cfg = cfg_i; + + prim_rom #( + .Width(Width), + .Depth(Depth), + .MemInitFile(MemInitFile) + ) u_prim_rom ( + .clk_i, + .req_i, + .addr_i, + .rdata_o + ); + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + rvalid_o <= 1'b0; + end else begin + rvalid_o <= req_i; + end + end + + //////////////// + // ASSERTIONS // + //////////////// + + // Control Signals should never be X + `ASSERT(noXOnCsI, !$isunknown(req_i), clk_i, '0) +endmodule : prim_rom_adv diff --git a/vendor/lowrisc_ip/prim/rtl/prim_util.svh b/vendor/lowrisc_ip/prim/rtl/prim_util.svh new file mode 100644 index 00000000..4543a377 --- /dev/null +++ b/vendor/lowrisc_ip/prim/rtl/prim_util.svh @@ -0,0 +1,47 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +// Utility macros + +`ifndef PRIM_UTIL_SVH +`define PRIM_UTIL_SVH + +/** + * Math function: Number of bits needed to address |value| items. + * + * 0 for value == 0 + * vbits = 1 for value == 1 + * ceil(log2(value)) for value > 1 + * + * + * The primary use case for this function is the definition of registers/arrays + * which are wide enough to contain |value| items. + * + * This function identical to $clog2() for all input values except the value 1; + * it could be considered an "enhanced" $clog2() function. + * + * + * Example 1: + * parameter Items = 1; + * localparam ItemsWidth = vbits(Items); // 1 + * logic [ItemsWidth-1:0] item_register; // items_register is now [0:0] + * + * Example 2: + * parameter Items = 64; + * localparam ItemsWidth = vbits(Items); // 6 + * logic [ItemsWidth-1:0] item_register; // items_register is now [5:0] + * + * Note: If you want to store the number "value" inside a register, you need + * a register with size vbits(value + 1), since you also need to store + * the number 0. + * + * Example 3: + * logic [vbits(64)-1:0] store_64_logic_values; // width is [5:0] + * logic [vbits(64 + 1)-1:0] store_number_64; // width is [6:0] + */ +function automatic integer vbits(integer value); + return (value == 1) ? 1 : $clog2(value); +endfunction + +`endif // PRIM_UTIL_SVH diff --git a/vendor/lowrisc_ip/prim_generic/rtl/prim_generic_flash.sv b/vendor/lowrisc_ip/prim_generic/rtl/prim_generic_flash.sv index 6a0655e0..682729c4 100644 --- a/vendor/lowrisc_ip/prim_generic/rtl/prim_generic_flash.sv +++ b/vendor/lowrisc_ip/prim_generic/rtl/prim_generic_flash.sv @@ -282,6 +282,4 @@ module prim_generic_flash #( .rdata_o (rd_data_o) ); - - endmodule // prim_generic_flash diff --git a/vendor/lowrisc_ip/prim_generic/rtl/prim_generic_rom.sv b/vendor/lowrisc_ip/prim_generic/rtl/prim_generic_rom.sv index 9bfe88b5..47cb80f8 100644 --- a/vendor/lowrisc_ip/prim_generic/rtl/prim_generic_rom.sv +++ b/vendor/lowrisc_ip/prim_generic/rtl/prim_generic_rom.sv @@ -11,27 +11,17 @@ module prim_generic_rom #( localparam int Aw = $clog2(Depth) ) ( - input clk_i, - input rst_ni, - input [Aw-1:0] addr_i, - input cs_i, - output logic [Width-1:0] dout_o, - output logic dvalid_o + input logic clk_i, + input logic req_i, + input logic [Aw-1:0] addr_i, + output logic [Width-1:0] rdata_o ); logic [Width-1:0] mem [Depth]; always_ff @(posedge clk_i) begin - if (cs_i) begin - dout_o <= mem[addr_i]; - end - end - - always_ff @(posedge clk_i or negedge rst_ni) begin - if (!rst_ni) begin - dvalid_o <= 1'b0; - end else begin - dvalid_o <= cs_i; + if (req_i) begin + rdata_o <= mem[addr_i]; end end @@ -42,5 +32,5 @@ module prim_generic_rom #( //////////////// // Control Signals should never be X - `ASSERT(noXOnCsI, !$isunknown(cs_i), clk_i, '0) + `ASSERT(noXOnCsI, !$isunknown(req_i), clk_i, '0) endmodule