This is manually squashed with a change to import dv_base_reg too, a
new module that was created by Weicai's "csr backdoor support" patch.
It's needed because it is a dependency of dv_lib.

Update code from upstream repository
https://github.com/lowRISC/opentitan to revision
c91b50f357a76dae2ada104e397f6a91f72a33da

* [prim_ram*_adv] Update core files and add prim_util dependency
  (Michael Schaffner)
* [prim_ram*_adv] Implement Byte parity in prim_ram*_adv (Michael
  Schaffner)
* [dvsim] Run tests in "interleaved" order (Rupert Swarbrick)
* [dvsim] Remove unnecessary getattr/setattr calls from SimCfg.py
  (Rupert Swarbrick)
* [dv] Add support for multiple ral models (Srikrishna Iyer)
* [rtl] Fix prim flash dependency (Srikrishna Iyer)
* [prim_fifo_sync] Make FIFO output zero when empty (Noah Moroze)
* [dv] csr backdoor support (Weicai Yang)
* [prim] Add a "clog2 width" function (Philipp Wagner)
* [dvsim] Allow max-parallel to be set in the environment (Rupert
  Swarbrick)
* [dvsim] Fix --reseed argument (Rupert Swarbrick)
* [prim_ram/rom*_adv] Break out into individual core files (Michael
  Schaffner)
* [prim_rom] Align port naming with prim_ram* (Michael Schaffner)
* [dv] Allow a test to have "simple" timestamps (Rupert Swarbrick)
* [dvsim] Improve --help message (Rupert Swarbrick)
* [dvsim] Remove unused --local argument (Rupert Swarbrick)
* [dvsim] Small tidy-ups to mode selection in SimCfg.py (Rupert
  Swarbrick)
* [fpv] formal compile fix required by VC Formal (Cindy Chen)
* [dvsim] Fix error detection logic in Deploy.py (Rupert Swarbrick)

Signed-off-by: Rupert Swarbrick <rswarbrick@lowrisc.org>
This commit is contained in:
Rupert Swarbrick 2020-06-12 11:15:45 +01:00 committed by Rupert Swarbrick
parent d79eb58ae5
commit 8c11bd780c
36 changed files with 1224 additions and 823 deletions

View file

@ -9,6 +9,6 @@
upstream:
{
url: https://github.com/lowRISC/opentitan
rev: d78da129c7f2b115ccabd1c3af199e0e5812f365
rev: c91b50f357a76dae2ada104e397f6a91f72a33da
}
}

View file

@ -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"},
]
}

View file

@ -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;

View file

@ -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

View file

@ -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,

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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);

View file

@ -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}

View file

@ -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"

View file

@ -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:<br>\n"
self.fail_msg += msg
log.log(VERBOSE, msg)

View file

@ -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

View file

@ -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="<cfg-hjson-file>",
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="<branch-name>",
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')

View file

@ -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:

View file

@ -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:

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

17
vendor/lowrisc_ip/prim/prim_util.core vendored Normal file
View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -282,6 +282,4 @@ module prim_generic_flash #(
.rdata_o (rd_data_o)
);
endmodule // prim_generic_flash

View file

@ -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