diff --git a/vendor/lowrisc_ip.lock.hjson b/vendor/lowrisc_ip.lock.hjson index ddbf16da..4dca714f 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: 7117c349d5465b5152d3bb774079013924a3e9ba + rev: da3ac7c4eb23a92194874ad2daf2e5f9e3330572 } } diff --git a/vendor/lowrisc_ip/dv/sv/csr_utils/csr_utils_pkg.sv b/vendor/lowrisc_ip/dv/sv/csr_utils/csr_utils_pkg.sv index 19546664..70dca5c8 100644 --- a/vendor/lowrisc_ip/dv/sv/csr_utils/csr_utils_pkg.sv +++ b/vendor/lowrisc_ip/dv/sv/csr_utils/csr_utils_pkg.sv @@ -60,28 +60,6 @@ package csr_utils_pkg; under_reset = 0; endfunction - // Get all valid csr addrs - useful to check if incoming addr falls in the csr range. - function automatic void get_csr_addrs(input uvm_reg_block ral, ref uvm_reg_addr_t csr_addrs[$]); - uvm_reg csrs[$]; - ral.get_registers(csrs); - foreach (csrs[i]) begin - csr_addrs.push_back(csrs[i].get_address()); - end - endfunction - - // Get all valid mem addr ranges - useful to check if incoming addr falls in the mem range. - function automatic void get_mem_addr_ranges(uvm_reg_block ral, ref addr_range_t mem_ranges[$]); - uvm_mem mems[$]; - ral.get_memories(mems); - foreach (mems[i]) begin - addr_range_t mem_range; - mem_range.start_addr = mems[i].get_address(); - mem_range.end_addr = mem_range.start_addr + - mems[i].get_size() * mems[i].get_n_bytes() - 1; - mem_ranges.push_back(mem_range); - end - endfunction - // get mem object from address function automatic uvm_mem get_mem_by_addr(uvm_reg_block ral, uvm_reg_addr_t addr); uvm_mem mem; diff --git a/vendor/lowrisc_ip/dv/sv/dv_base_reg/dv_base_reg_block.sv b/vendor/lowrisc_ip/dv/sv/dv_base_reg/dv_base_reg_block.sv index a71aefb9..8538ec33 100644 --- a/vendor/lowrisc_ip/dv/sv/dv_base_reg/dv_base_reg_block.sv +++ b/vendor/lowrisc_ip/dv/sv/dv_base_reg/dv_base_reg_block.sv @@ -16,6 +16,15 @@ class dv_base_reg_block extends uvm_reg_block; // This is set by compute_addr_mask(), which must run after locking the model. protected uvm_reg_addr_t addr_mask[uvm_reg_map]; + uvm_reg_addr_t csr_addrs[$]; + + addr_range_t mem_ranges[$]; + + addr_range_t mapped_addr_ranges[$]; + + bit has_unmapped_addrs; + addr_range_t unmapped_addr_ranges[$]; + function new (string name = "", int has_coverage = UVM_NO_COVERAGE); super.new(name, has_coverage); endfunction @@ -94,6 +103,98 @@ class dv_base_reg_block extends uvm_reg_block; `DV_CHECK_FATAL(addr_mask[map]) endfunction + // Internal function, used to get a list of all valid CSR addresses. + protected function void compute_csr_addrs(); + uvm_reg csrs[$]; + get_registers(csrs); + foreach (csrs[i]) begin + csr_addrs.push_back(csrs[i].get_address()); + end + endfunction + + // Internal function, used to get a list of all valid memory ranges + protected function void compute_mem_addr_ranges(); + uvm_mem mems[$]; + get_memories(mems); + foreach (mems[i]) begin + addr_range_t mem_range; + mem_range.start_addr = mems[i].get_address(); + mem_range.end_addr = mem_range.start_addr + + mems[i].get_size() * mems[i].get_n_bytes() - 1; + mem_ranges.push_back(mem_range); + end + endfunction + + // Used to get a list of all valid address ranges covered by this reg block + function void compute_mapped_addr_ranges(); + uvm_reg csrs[$]; + get_registers(csrs); + + // Compute all CSR addresses and mem ranges known to this reg block + compute_csr_addrs(); + compute_mem_addr_ranges(); + + // Convert each CSR into an address range + foreach (csrs[i]) begin + addr_range_t csr_addr_range; + csr_addr_range.start_addr = csrs[i].get_address(); + csr_addr_range.end_addr = csr_addr_range.start_addr + csrs[i].get_n_bytes() - 1; + mapped_addr_ranges.push_back(csr_addr_range); + end + + mapped_addr_ranges = {mapped_addr_ranges, mem_ranges}; + + // Sort the mapped address ranges in ascending order based on the start_addr of each range + mapped_addr_ranges.sort(m) with (m.start_addr); + + endfunction + + // Used to get a list of all invalid address ranges in this reg block + function void compute_unmapped_addr_ranges(); + addr_range_t range; + + // convert the address mask into a relative address, + // this is the highest addressable location in the register block + uvm_reg_addr_t highest_addr = default_map.get_base_addr() + get_addr_mask(); + + // unmapped address ranges consist of: + // - the address space between all mapped address ranges (if exists) + // - space between csr_base_addr and the first mapped address range (if exists) + // - space between the last mapped address and the highest address mapped by the address mask + if (mapped_addr_ranges.size() == 0) begin + range.start_addr = default_map.get_base_addr(); + range.end_addr = highest_addr; + unmapped_addr_ranges.push_back(range); + end else begin + // 0 -> start_addr-1 + if (mapped_addr_ranges[0].start_addr - default_map.get_base_addr() > 0) begin + range.start_addr = default_map.get_base_addr(); + range.end_addr = mapped_addr_ranges[0].start_addr - 1; + unmapped_addr_ranges.push_back(range); + end + + // all address ranges in the "middle" - only applies if + // there are more than 1 mapped address ranges + if (mapped_addr_ranges.size() > 1) begin + for (int i = 0; i < mapped_addr_ranges.size() - 1; i++) begin + range.start_addr = mapped_addr_ranges[i].end_addr + 1; + range.end_addr = mapped_addr_ranges[i+1].start_addr - 1; + if (range.start_addr < range.end_addr) begin + unmapped_addr_ranges.push_back(range); + end + end + end + + // end_addr+1 -> highest_addr + if (mapped_addr_ranges[$].end_addr < highest_addr) begin + range.start_addr = mapped_addr_ranges[$].end_addr + 1; + range.end_addr = highest_addr; + unmapped_addr_ranges.push_back(range); + end + end + has_unmapped_addrs = (unmapped_addr_ranges.size() > 0); + endfunction + // Return the offset of the highest byte contained in either a register or a memory function uvm_reg_addr_t get_max_offset(uvm_reg_map map = null); uvm_reg_addr_t max_offset; @@ -143,6 +244,7 @@ class dv_base_reg_block extends uvm_reg_block; if (map == null) map = get_default_map(); mask = get_addr_mask(map); + // If base_addr is '1, randomly pick an aligned base address if (base_addr == '1) begin `DV_CHECK_STD_RANDOMIZE_WITH_FATAL(base_addr, (base_addr & mask) == '0;) diff --git a/vendor/lowrisc_ip/dv/sv/dv_lib/dv_base_env_cfg.sv b/vendor/lowrisc_ip/dv/sv/dv_lib/dv_base_env_cfg.sv index 11d24f1f..7d0fe2eb 100644 --- a/vendor/lowrisc_ip/dv/sv/dv_lib/dv_base_env_cfg.sv +++ b/vendor/lowrisc_ip/dv/sv/dv_lib/dv_base_env_cfg.sv @@ -17,7 +17,7 @@ class dv_base_env_cfg #(type RAL_T = dv_base_reg_block) extends uvm_object; // useful to keep the runtime down especially in time-sensitive runs such as CI, which is meant // to check the code health and not find design bugs. It is set via plusarg and retrieved in // `dv_base_test`. - bit smoke_test = 0; + bit smoke_test = 0; // bit to configure all uvcs with zero delays to create high bw test rand bit zero_delays; @@ -38,9 +38,6 @@ class dv_base_env_cfg #(type RAL_T = dv_base_reg_block) extends uvm_object; // super.initialize(csr_base_addr); string ral_model_names[$] = {RAL_T::type_name}; - bit [bus_params_pkg::BUS_AW-1:0] csr_addrs[string][$]; - addr_range_t mem_ranges[string][$]; - // clk_rst_if & freq // clk_rst_vif and clk_freq_mhz are used for default clk/rst. If more than one RAL, the other // clk_rst_vif and clk_freq_mhz can be found from the associative arrays @@ -50,10 +47,10 @@ class dv_base_env_cfg #(type RAL_T = dv_base_reg_block) extends uvm_object; rand clk_freq_mhz_e clk_freqs_mhz[string]; `uvm_object_param_utils_begin(dv_base_env_cfg #(RAL_T)) - `uvm_field_int (is_active, UVM_DEFAULT) - `uvm_field_int (en_scb, UVM_DEFAULT) - `uvm_field_int (en_cov, UVM_DEFAULT) - `uvm_field_int (zero_delays, UVM_DEFAULT) + `uvm_field_int (is_active, UVM_DEFAULT) + `uvm_field_int (en_scb, UVM_DEFAULT) + `uvm_field_int (en_cov, UVM_DEFAULT) + `uvm_field_int (zero_delays, UVM_DEFAULT) `uvm_object_utils_end `uvm_object_new @@ -98,6 +95,7 @@ class dv_base_env_cfg #(type RAL_T = dv_base_reg_block) extends uvm_object; endfunction virtual function void create_ral_models(bit [bus_params_pkg::BUS_AW-1:0] csr_base_addr = '1); + foreach (ral_model_names[i]) begin string ral_name = ral_model_names[i]; uvm_reg_addr_t base_addr; @@ -123,8 +121,16 @@ class dv_base_env_cfg #(type RAL_T = dv_base_reg_block) extends uvm_object; reg_blk.set_base_addr(base_addr); // Get list of valid csr addresses (useful in seq to randomize addr as well as in scb checks) - get_csr_addrs(reg_blk, csr_addrs[ral_name]); - get_mem_addr_ranges(reg_blk, mem_ranges[ral_name]); + reg_blk.compute_mapped_addr_ranges(); + reg_blk.compute_unmapped_addr_ranges(); + `uvm_info(msg_id, + $sformatf("RAL[%0s] mapped addresses: %0p", + ral_name, reg_blk.mapped_addr_ranges), + UVM_HIGH) + `uvm_info(msg_id, + $sformatf("RAL[%0s] unmapped addresses: %0p", + ral_name, reg_blk.unmapped_addr_ranges), + UVM_HIGH) ral_models[ral_name] = reg_blk; end diff --git a/vendor/lowrisc_ip/dv/sv/dv_lib/dv_base_scoreboard.sv b/vendor/lowrisc_ip/dv/sv/dv_lib/dv_base_scoreboard.sv index d443322f..facd89db 100644 --- a/vendor/lowrisc_ip/dv/sv/dv_lib/dv_base_scoreboard.sv +++ b/vendor/lowrisc_ip/dv/sv/dv_lib/dv_base_scoreboard.sv @@ -25,6 +25,7 @@ class dv_base_scoreboard #(type RAL_T = dv_base_reg_block, super.run_phase(phase); fork monitor_reset(); + sample_resets(); join_none endtask @@ -33,12 +34,10 @@ class dv_base_scoreboard #(type RAL_T = dv_base_reg_block, if (!cfg.clk_rst_vif.rst_n) begin `uvm_info(`gfn, "reset occurred", UVM_HIGH) cfg.reset_asserted(); - csr_utils_pkg::reset_asserted(); @(posedge cfg.clk_rst_vif.rst_n); reset(); cfg.reset_deasserted(); csr_utils_pkg::clear_outstanding_access(); - csr_utils_pkg::reset_deasserted(); `uvm_info(`gfn, "out of reset", UVM_HIGH) end else begin @@ -48,6 +47,10 @@ class dv_base_scoreboard #(type RAL_T = dv_base_reg_block, end endtask + virtual task sample_resets(); + // Do nothing, actual coverage collection is under extended classes. + endtask + virtual function void reset(string kind = "HARD"); // reset the ral model foreach (cfg.ral_models[i]) cfg.ral_models[i].reset(kind); diff --git a/vendor/lowrisc_ip/dv/sv/dv_lib/dv_base_test.sv b/vendor/lowrisc_ip/dv/sv/dv_lib/dv_base_test.sv index 25ad402c..c60ecdbd 100644 --- a/vendor/lowrisc_ip/dv/sv/dv_lib/dv_base_test.sv +++ b/vendor/lowrisc_ip/dv/sv/dv_lib/dv_base_test.sv @@ -14,7 +14,7 @@ class dv_base_test #(type CFG_T = dv_base_env_cfg, uint max_quit_count = 1; uint64 test_timeout_ns = 200_000_000; // 200ms uint drain_time_ns = 2_000; // 2us - bit poll_for_stop = 1'b1; + bit poll_for_stop = 1'b0; uint poll_for_stop_interval_ns = 1000; `uvm_component_new diff --git a/vendor/lowrisc_ip/dv/sv/dv_lib/dv_base_vseq.sv b/vendor/lowrisc_ip/dv/sv/dv_lib/dv_base_vseq.sv index cdfd8c65..a4946b05 100644 --- a/vendor/lowrisc_ip/dv/sv/dv_lib/dv_base_vseq.sv +++ b/vendor/lowrisc_ip/dv/sv/dv_lib/dv_base_vseq.sv @@ -86,7 +86,7 @@ class dv_base_vseq #(type RAL_T = dv_base_reg_block, virtual task apply_reset(string kind = "HARD"); if (kind == "HARD") begin - if (cfg.clk_rst_vifs.size > 0) begin + if (cfg.clk_rst_vifs.size() > 0) begin fork begin : isolation_fork foreach (cfg.clk_rst_vifs[i]) begin @@ -104,6 +104,39 @@ class dv_base_vseq #(type RAL_T = dv_base_reg_block, end // if (kind == "HARD") endtask + // Apply all resets in the DUT concurrently to generate a random in-test reset scenario. + // + // - Assert resets concurrently to make sure all resets are issued. + // - Deassert resets concurrently is a specific requirement of the `stress_all_with_rand_reset` + // sequence, which will randomly issue resets and terminate the parallel sequence once all DUT + // resets are deasserted. If DUT resets are deasserted at different time, the parallel sequence + // might send a transaction request to driver between different resets are deasserting. Then when + // `stress_all_with_rand_reset` sequence tries to terminate the parallel sequence, an UVM_ERROR + // will be thrown by the sequencer saying `task responsible for requesting a wait_for_grant has + // been killed`. + // In order to ensure all resets at least being asserted for one clock cycle, this task takes an + // optional input `reset_duration_ps` if the DUT has additional resets. The task uses this input + // to compute the minimal time required to keep all resets asserted. + virtual task apply_resets_concurrently(int reset_duration_ps = 0); + + // Has one or more RAL models in DUT. + if (cfg.clk_rst_vifs.size() > 0) begin + foreach (cfg.clk_rst_vifs[i]) begin + cfg.clk_rst_vifs[i].drive_rst_pin(0); + reset_duration_ps = max2(reset_duration_ps, cfg.clk_rst_vifs[i].clk_period_ps); + end + #(reset_duration_ps * $urandom_range(2, 10) * 1ps); + foreach (cfg.clk_rst_vifs[i]) cfg.clk_rst_vifs[i].drive_rst_pin(1); + + // No RAL model and only has default clk_rst_vif. + end else begin + cfg.clk_rst_vif.drive_rst_pin(0); + reset_duration_ps = max2(reset_duration_ps, cfg.clk_rst_vif.clk_period_ps); + #(reset_duration_ps * $urandom_range(2, 10) * 1ps); + cfg.clk_rst_vif.drive_rst_pin(1); + end + endtask + virtual task wait_for_reset(string reset_kind = "HARD", bit wait_for_assert = 1, bit wait_for_deassert = 1); @@ -179,7 +212,11 @@ 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 = cfg.ral_models; + // We have to assign this array in a loop because the element types aren't equivalent, so + // the array types aren't assignment compatible. + foreach (cfg.ral_models[i]) begin + m_csr_write_seq.models[i] = cfg.ral_models[i]; + end m_csr_write_seq.external_checker = cfg.en_scb; m_csr_write_seq.en_rand_backdoor_write = 1'b1; m_csr_write_seq.start(null); @@ -196,7 +233,11 @@ 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.models = cfg.ral_models; + // We have to assign this array in a loop because the element types aren't equivalent, so + // the array types aren't assignment compatible. + foreach (cfg.ral_models[i]) begin + m_csr_seq.models[i] = cfg.ral_models[i]; + end m_csr_seq.external_checker = cfg.en_scb; m_csr_seq.num_test_csrs = num_test_csrs; m_csr_seq.start(null); diff --git a/vendor/lowrisc_ip/dv/sv/dv_utils/dv_utils_pkg.sv b/vendor/lowrisc_ip/dv/sv/dv_utils/dv_utils_pkg.sv index d411b5ef..800461a0 100644 --- a/vendor/lowrisc_ip/dv/sv/dv_utils/dv_utils_pkg.sv +++ b/vendor/lowrisc_ip/dv/sv/dv_utils/dv_utils_pkg.sv @@ -14,8 +14,11 @@ package dv_utils_pkg; `endif // common parameters used across all benches - parameter int NUM_MAX_INTERRUPTS = 32; - parameter int NUM_MAX_ALERTS = 32; + parameter int NUM_MAX_INTERRUPTS = 32; + typedef logic [NUM_MAX_INTERRUPTS-1:0] interrupt_t; + + parameter int NUM_MAX_ALERTS = 32; + typedef logic [NUM_MAX_ALERTS-1:0] alert_t; // types & variables typedef bit [31:0] uint; @@ -24,6 +27,13 @@ package dv_utils_pkg; typedef bit [31:0] uint32; typedef bit [63:0] uint64; + // TODO: The above typedefs violate the name rule, which is fixed below. Cleanup the codebase to + // use the typedefs below and remove the ones above. + typedef bit [7:0] uint8_t; + typedef bit [15:0] uint16_t; + typedef bit [31:0] uint32_t; + typedef bit [63:0] uint64_t; + // typedef parameterized pins_if for ease of implementation for interrupts and alerts typedef virtual pins_if #(NUM_MAX_INTERRUPTS) intr_vif; typedef virtual pins_if #(1) devmode_vif; @@ -87,6 +97,16 @@ package dv_utils_pkg; return (a > b) ? a : b; endfunction + // return the biggest value within the given queue of integers. + function automatic int max(const ref int int_q[$]); + `DV_CHECK_GT_FATAL(int_q.size(), 0, "max function cannot accept an empty queue of integers!", + msg_id) + // Assign the first value from the queue in case of negative integers. + max = int_q[0]; + foreach (int_q[i]) max = max2(max, int_q[i]); + return max; + endfunction + // get absolute value of the input. Usage: absolute(val) or absolute(a - b) function automatic uint absolute(int val); return val >= 0 ? val : -val; @@ -182,7 +202,7 @@ package dv_utils_pkg; // Periodically check for the existence of a magic file (dv.stop). Exit if it exists. This // provides a mechanism to gracefully kill a simulation without direct access to the process. - task automatic poll_for_stop(uint interval_ns = 1000, string filename = "dv.stop"); + task automatic poll_for_stop(uint interval_ns = 10_000, string filename = "dv.stop"); fork while (1) begin #(interval_ns * 1ns); diff --git a/vendor/lowrisc_ip/dv/tools/dvsim/common_sim_cfg.hjson b/vendor/lowrisc_ip/dv/tools/dvsim/common_sim_cfg.hjson index ee51b10a..6d7a44cd 100644 --- a/vendor/lowrisc_ip/dv/tools/dvsim/common_sim_cfg.hjson +++ b/vendor/lowrisc_ip/dv/tools/dvsim/common_sim_cfg.hjson @@ -21,7 +21,9 @@ // pass and fail patterns build_pass_patterns: [] - build_fail_patterns: ["^ERROR:.*$"] // fusesoc build error + // TODO: Add back FuseSoC fail pattern after + // https://github.com/lowRISC/opentitan/issues/7348 is resolved. + build_fail_patterns: [] run_pass_patterns: ["^TEST PASSED (UVM_)?CHECKS$"] run_fail_patterns: ["^UVM_ERROR\\s[^:].*$", "^UVM_FATAL\\s[^:].*$", diff --git a/vendor/lowrisc_ip/dv/tools/dvsim/testplans/tl_device_access_types_testplan.hjson b/vendor/lowrisc_ip/dv/tools/dvsim/testplans/tl_device_access_types_testplan.hjson index 20eea5d4..b471c112 100644 --- a/vendor/lowrisc_ip/dv/tools/dvsim/testplans/tl_device_access_types_testplan.hjson +++ b/vendor/lowrisc_ip/dv/tools/dvsim/testplans/tl_device_access_types_testplan.hjson @@ -12,21 +12,22 @@ { name: tl_d_illegal_access desc: '''Drive unsupported requests via TL interface and verify correctness of response - / behavior. Below error cases are tested + / behavior. Below error cases are tested bases on the + [TLUL spec]({{< relref "hw/ip/tlul/doc/_index.md#explicit-error-cases" >}}) - TL-UL protocol error cases - - Unsupported opcode. e.g a_opcode isn't Get, PutPartialData or PutFullData - - Mask isn't all active if opcode = PutFullData - - Mask isn't in enabled lanes, e.g. a_address = 0x00, a_size = 0, a_mask = 'b0010 - - Mask doesn't align with address, e.g. a_address = 0x01, a_mask = 'b0001 - - Address and size aren't aligned, e.g. a_address = 0x01, a_size != 0 - - Size is over 2. + - invalid opcode + - some mask bits not set when opcode is `PutFullData` + - mask does not match the transfer size, e.g. `a_address = 0x00`, `a_size = 0`, + `a_mask = 'b0010` + - mask and address misaligned, e.g. `a_address = 0x01`, `a_mask = 'b0001` + - address and size aren't aligned, e.g. `a_address = 0x01`, `a_size != 0` + - size is greater than 2 - OpenTitan defined error cases - - Access unmapped address, return d_error = 1 when devmode_i == 1 - - Write CSR with unaligned address, e.g. a_address[1:0] != 0 - - Write CSR less than its width, e.g. when CSR is 2 bytes wide, only write 1 byte - - Write a memory without enabling all lanes (a_mask = '1) if memory doesn't support - byte enabled write - - Read a WO (write-only) memory''' + - access unmapped address, expect `d_error = 1` when `devmode_i == 1` + - write a CSR with unaligned address, e.g. `a_address[1:0] != 0` + - write a CSR less than its width, e.g. when CSR is 2 bytes wide, only write 1 byte + - write a memory with `a_mask != '1` when it doesn't support partial accesses + - read a WO (write-only) memory''' milestone: V2 tests: ["{name}_tl_errors"] } @@ -43,15 +44,24 @@ } { name: tl_d_partial_access - desc: '''Access CSR with one or more bytes of data - For read, expect to return all word value of the CSR - For write, enabling bytes should cover all CSR valid fields''' + desc: '''Access CSR with one or more bytes of data. + For read, expect to return all word value of the CSR. + For write, enabling bytes should cover all CSR valid fields.''' milestone: V2 tests: ["{name}_csr_hw_reset", "{name}_csr_rw", "{name}_csr_aliasing", "{name}_same_csr_outstanding"] } + { + name: tl_intg_err + desc: ''' Verify that the data integrity check violation generates an alert. + + Randomly inject errors on the control, data, or the ECC bits during CSR accesses. + Verify that triggers the correct fatal alert.''' + milestone: V3 + tests: ["{name}_tl_intg_err"] + } ] } diff --git a/vendor/lowrisc_ip/dv/tools/dvsim/tests/tl_access_tests.hjson b/vendor/lowrisc_ip/dv/tools/dvsim/tests/tl_access_tests.hjson index 60d656f6..28aeaa37 100644 --- a/vendor/lowrisc_ip/dv/tools/dvsim/tests/tl_access_tests.hjson +++ b/vendor/lowrisc_ip/dv/tools/dvsim/tests/tl_access_tests.hjson @@ -16,5 +16,14 @@ run_opts: ["+run_tl_errors"] reseed: 20 } + { + name: "{name}_tl_intg_err" + build_mode: "cover_reg_top" + uvm_test_seq: "{name}_common_vseq" + run_opts: ["+run_tl_intg_err", "+en_scb=0"] + // TODO, lower the reseed to avoid having many failed tests in regression as not all the IPs + // have the integrity alert connected + reseed: 1 + } ] } diff --git a/vendor/lowrisc_ip/dv/tools/dvsim/vcs.hjson b/vendor/lowrisc_ip/dv/tools/dvsim/vcs.hjson index 44eee432..04908a05 100644 --- a/vendor/lowrisc_ip/dv/tools/dvsim/vcs.hjson +++ b/vendor/lowrisc_ip/dv/tools/dvsim/vcs.hjson @@ -48,7 +48,10 @@ // - Read capability on registers, variables, and nets // - Write (deposit) capability on registers and variables // - Force capability on registers, variables, and nets + // TODO Trim this flag since +f hurts performance. "-debug_access+f", + // This option is needed for uvm_hdl_*, when it accesses the array under `celldefine + "-debug_region=cell+lib", // Use this to conditionally compile for VCS (example: LRM interpretations differ // across tools). "+define+VCS", @@ -125,31 +128,47 @@ cov_merge_dir: "{scratch_path}/cov_merge" cov_merge_db_dir: "{cov_merge_dir}/merged.vdb" cov_merge_cmd: "{job_prefix} urg" - cov_merge_opts: ["-full64", - "+urg+lic+wait", - "-nocheck", + cov_merge_opts: ["-lca", + "-full64", + // No need of generating report when merging coverage. "-noreport", - "-flex_merge drop", - "-group merge_across_scopes", + // Merge results from tests in parallel. "-parallel", "-parallel_split 20", + "-parallel_temproot {cov_merge_dir}", + "+urg+lic+wait", + // Merge same assert instances found in different VDBs. + "-merge_across_libs", + // This enables grading on the merged vdb. + "-show tests", + // Enable union mode of flexible merging for covergroups. + "-flex_merge union", // Use cov_db_dirs var for dir args; append -dir in front of each "{eval_cmd} echo {cov_db_dirs} | sed -E 's/(\\S+)/-dir \\1/g'", "-dbname {cov_merge_db_dir}"] // Generate coverage reports in text as well as html. - cov_report_dir: "{scratch_path}/cov_report" - cov_report_cmd: "{job_prefix} urg" - cov_report_opts: ["-full64", - "+urg+lic+wait", - "-dir {cov_merge_db_dir}", - "-group instcov_for_score", - "-line nocasedef", - "-format both", - "-elfile {vcs_cov_excl_files}", - "-report {cov_report_dir}"] - cov_report_txt: "{cov_report_dir}/dashboard.txt" - cov_report_page: "dashboard.html" + cov_report_dir: "{scratch_path}/cov_report" + cov_report_cmd: "{job_prefix} urg" + cov_report_opts: ["-lca", + "-full64", + "+urg+lic+wait", + // Lists all the tests that covered a given object. + "-show tests", + // Enable test grading. + "-grade index", + // Use simple ratio of total covered bins over total bins across cps & crs, + "-group ratio", + // Compute overall coverage for per-instance covergroups individually rather + // than cumulatively. + "-group instcov_for_score", + "-dir {cov_merge_db_dir}", + "-line nocasedef", + "-format both", + "-elfile {vcs_cov_excl_files}", + "-report {cov_report_dir}"] + cov_report_txt: "{cov_report_dir}/dashboard.txt" + cov_report_page: "dashboard.html" // UNR related. // All code coverage, assert isn't supported @@ -227,7 +246,7 @@ { name: vcs_waves is_sim_mode: 1 - build_opts: ["-debug_access+all"] + build_opts: ["-debug_access"] } { name: vcs_cov diff --git a/vendor/lowrisc_ip/dv/tools/vcs/cover.cfg b/vendor/lowrisc_ip/dv/tools/vcs/cover.cfg index b42316af..89a44b99 100644 --- a/vendor/lowrisc_ip/dv/tools/vcs/cover.cfg +++ b/vendor/lowrisc_ip/dv/tools/vcs/cover.cfg @@ -9,9 +9,13 @@ -module pins_if // DV construct. -module clk_rst_if // DV construct. -moduletree prim_alert_sender // prim_alert_sender is verified in FPV. +-moduletree prim_prince // prim_prince is verified in a separate DV environment. +-moduletree prim_lfsr // prim_lfsr is verified in FPV. begin tgl -tree tb +tree tb.dut 1 +module prim_alert_sender + +module prim_prince + +module prim_lfsr end diff --git a/vendor/lowrisc_ip/dv/verilator/cpp/dpi_memutil.cc b/vendor/lowrisc_ip/dv/verilator/cpp/dpi_memutil.cc index df2d8a4c..c57a3eec 100644 --- a/vendor/lowrisc_ip/dv/verilator/cpp/dpi_memutil.cc +++ b/vendor/lowrisc_ip/dv/verilator/cpp/dpi_memutil.cc @@ -431,7 +431,7 @@ void DpiMemUtil::LoadElfToMemories(bool verbose, const std::string &filepath) { const MemArea &mem_area = *mem_areas_[mem_area_it->second]; - for (const auto seg_pr : staged_mem.GetSegs()) { + for (const auto &seg_pr : staged_mem.GetSegs()) { const AddrRange &seg_rng = seg_pr.first; const std::vector &seg_data = seg_pr.second; @@ -458,6 +458,9 @@ void DpiMemUtil::StageElf(bool verbose, const std::string &path) { ElfFile elf(path); + // Allow subclasses to get at the loaded ELF data if they need it + OnElfLoaded(elf.ptr_); + size_t file_size; const char *file_data = elf_rawfile(elf.ptr_, &file_size); assert(file_data); diff --git a/vendor/lowrisc_ip/dv/verilator/cpp/dpi_memutil.h b/vendor/lowrisc_ip/dv/verilator/cpp/dpi_memutil.h index 813f08b6..a41aae99 100644 --- a/vendor/lowrisc_ip/dv/verilator/cpp/dpi_memutil.h +++ b/vendor/lowrisc_ip/dv/verilator/cpp/dpi_memutil.h @@ -13,6 +13,9 @@ #include "mem_area.h" #include "ranged_map.h" +// Forward declaration for the Elf type from libelf. +struct Elf; + enum MemImageType { kMemImageUnknown = 0, kMemImageElf, @@ -59,6 +62,8 @@ class StagedMem { */ class DpiMemUtil { public: + virtual ~DpiMemUtil() {} + /** * Register a memory as instantiated by generic ram * @@ -128,6 +133,14 @@ class DpiMemUtil { */ const StagedMem &GetMemoryData(const std::string &mem_name) const; + protected: + /** + * A hook for subclasses to do extra computations with loaded ELF data. This + * runs as part of StageElf: after loading the ELF file, but before reading + * in the segments. + */ + virtual void OnElfLoaded(Elf *elf_file) {} + private: // Memory area registry. The maps give indices pointing into the vectors // (which all have the same number of elements). Note that mem_areas_ does diff --git a/vendor/lowrisc_ip/dv/verilator/cpp/ecc32_mem_area.cc b/vendor/lowrisc_ip/dv/verilator/cpp/ecc32_mem_area.cc index 75521a9e..294a7043 100644 --- a/vendor/lowrisc_ip/dv/verilator/cpp/ecc32_mem_area.cc +++ b/vendor/lowrisc_ip/dv/verilator/cpp/ecc32_mem_area.cc @@ -31,7 +31,7 @@ void Ecc32MemArea::LoadVmem(const std::string &path) const { void Ecc32MemArea::WriteBuffer(uint8_t buf[SV_MEM_WIDTH_BYTES], const std::vector &data, - size_t start_idx) const { + size_t start_idx, uint32_t dst_word) const { int log_width_32 = width_byte_ / 4; int phy_width_bits = 39 * log_width_32; int phy_width_bytes = (phy_width_bits + 7) / 8; @@ -116,7 +116,8 @@ void Ecc32MemArea::WriteBuffer(uint8_t buf[SV_MEM_WIDTH_BYTES], } void Ecc32MemArea::ReadBuffer(std::vector &data, - const uint8_t buf[SV_MEM_WIDTH_BYTES]) const { + const uint8_t buf[SV_MEM_WIDTH_BYTES], + uint32_t src_word) const { for (int i = 0; i < width_byte_; ++i) { int in_word = i / 4; diff --git a/vendor/lowrisc_ip/dv/verilator/cpp/ecc32_mem_area.h b/vendor/lowrisc_ip/dv/verilator/cpp/ecc32_mem_area.h index b0ddc10e..9b0523be 100644 --- a/vendor/lowrisc_ip/dv/verilator/cpp/ecc32_mem_area.h +++ b/vendor/lowrisc_ip/dv/verilator/cpp/ecc32_mem_area.h @@ -24,13 +24,14 @@ class Ecc32MemArea : public MemArea { void LoadVmem(const std::string &path) const override; - private: + protected: void WriteBuffer(uint8_t buf[SV_MEM_WIDTH_BYTES], - const std::vector &data, - size_t start_idx) const override; + const std::vector &data, size_t start_idx, + uint32_t dst_word) const override; void ReadBuffer(std::vector &data, - const uint8_t buf[SV_MEM_WIDTH_BYTES]) const override; + const uint8_t buf[SV_MEM_WIDTH_BYTES], + uint32_t src_word) const override; }; #endif // OPENTITAN_HW_DV_VERILATOR_CPP_ECC32_MEM_AREA_H_ diff --git a/vendor/lowrisc_ip/dv/verilator/cpp/mem_area.cc b/vendor/lowrisc_ip/dv/verilator/cpp/mem_area.cc index fd91db1e..1687840b 100644 --- a/vendor/lowrisc_ip/dv/verilator/cpp/mem_area.cc +++ b/vendor/lowrisc_ip/dv/verilator/cpp/mem_area.cc @@ -27,10 +27,6 @@ MemArea::MemArea(const std::string &scope, uint32_t num_words, void MemArea::Write(uint32_t word_offset, const std::vector &data) const { - // If this fails to set scope, it will throw an error which should - // be caught at this function's callsite. - SVScoped scoped(scope_); - // This "mini buffer" is used to transfer each write to SystemVerilog. // `simutil_set_mem` takes a fixed SV_MEM_WIDTH_BITS-bit vector but it will // only use the bits required for the RAM width. As an example, for a 32-bit @@ -47,8 +43,17 @@ void MemArea::Write(uint32_t word_offset, for (uint32_t i = 0; i < data_words; ++i) { uint32_t dst_word = word_offset + i; - WriteBuffer(minibuf, data, i * width_byte_); - if (!simutil_set_mem(dst_word, (svBitVecVal *)minibuf)) { + uint32_t phys_addr = ToPhysAddr(dst_word); + + WriteBuffer(minibuf, data, i * width_byte_, dst_word); + + // Both ToPhysAddr and WriteBuffer might set the scope with `SVScoped` so + // only construct `SVScoped` once they've both been called so they don't + // interact causing incorrect relative path behaviour. If this fails to set + // scope, it will throw an error which should be caught at this function's + // callsite. + SVScoped scoped(scope_); + if (!simutil_set_mem(phys_addr, (svBitVecVal *)minibuf)) { std::ostringstream oss; oss << "Could not set memory at byte offset 0x" << std::hex << dst_word * width_byte_ << "."; @@ -64,8 +69,6 @@ std::vector MemArea::Read(uint32_t word_offset, uint32_t num_bytes = width_byte_ * num_words; assert(num_words <= num_bytes); - SVScoped scoped(scope_); - // See Write for an explanation for this buffer. uint8_t minibuf[SV_MEM_WIDTH_BYTES]; memset(minibuf, 0, sizeof minibuf); @@ -76,13 +79,24 @@ std::vector MemArea::Read(uint32_t word_offset, for (uint32_t i = 0; i < num_words; ++i) { uint32_t src_word = word_offset + i; - if (!simutil_get_mem(src_word, (svBitVecVal *)minibuf)) { - std::ostringstream oss; - oss << "Could not read memory at byte offset 0x" << std::hex - << src_word * width_byte_ << "."; - throw std::runtime_error(oss.str()); + uint32_t phys_addr = ToPhysAddr(src_word); + + { + // Both ToPhysAddr and ReadBuffer might set the scope with `SVScoped`. + // Keep the `SVScoped` here confined to an inner scope so they don't + // interact causing incorrect relative path behaviour. If this fails to + // set scope, it will throw an error which should be caught at this + // function's callsite. + SVScoped scoped(scope_); + if (!simutil_get_mem(phys_addr, (svBitVecVal *)minibuf)) { + std::ostringstream oss; + oss << "Could not read memory at byte offset 0x" << std::hex + << src_word * width_byte_ << "."; + throw std::runtime_error(oss.str()); + } } - ReadBuffer(ret, minibuf); + + ReadBuffer(ret, minibuf, src_word); } return ret; @@ -95,8 +109,8 @@ void MemArea::LoadVmem(const std::string &path) const { } void MemArea::WriteBuffer(uint8_t buf[SV_MEM_WIDTH_BYTES], - const std::vector &data, - size_t start_idx) const { + const std::vector &data, size_t start_idx, + uint32_t dst_word) const { size_t words_left = data.size() - start_idx; size_t to_copy = std::min(words_left, (size_t)width_byte_); if (to_copy < width_byte_) { @@ -106,7 +120,8 @@ void MemArea::WriteBuffer(uint8_t buf[SV_MEM_WIDTH_BYTES], } void MemArea::ReadBuffer(std::vector &data, - const uint8_t buf[SV_MEM_WIDTH_BYTES]) const { + const uint8_t buf[SV_MEM_WIDTH_BYTES], + uint32_t src_word) const { // Append the first width_byte_ bytes of buf to data. std::copy_n(reinterpret_cast(buf), width_byte_, std::back_inserter(data)); diff --git a/vendor/lowrisc_ip/dv/verilator/cpp/mem_area.h b/vendor/lowrisc_ip/dv/verilator/cpp/mem_area.h index de3d3094..b73d4310 100644 --- a/vendor/lowrisc_ip/dv/verilator/cpp/mem_area.h +++ b/vendor/lowrisc_ip/dv/verilator/cpp/mem_area.h @@ -92,10 +92,11 @@ class MemArea { * @param buf Destination buffer * @param data A large buffer that contains the data to be written * @param start_idx An offset into \p data for the start of the memory word + * @param dst_word Logical address of the location being written */ virtual void WriteBuffer(uint8_t buf[SV_MEM_WIDTH_BYTES], - const std::vector &data, - size_t start_idx) const; + const std::vector &data, size_t start_idx, + uint32_t dst_word) const; /** Extract the logical memory contents corresponding to the physical * memory contents in \p buf and append them to \p data. @@ -104,13 +105,26 @@ class MemArea { * across. Other implementations might undo scrambling, remove ECC bits or * similar. * - * @param data The target, onto which the extracted memory contents should be - * appended. + * @param data The target, onto which the extracted memory contents should + * be appended. * - * @param buf Source buffer (physical memory bits) + * @param buf Source buffer (physical memory bits) + * @param src_word Logical address of the location being read */ virtual void ReadBuffer(std::vector &data, - const uint8_t buf[SV_MEM_WIDTH_BYTES]) const; + const uint8_t buf[SV_MEM_WIDTH_BYTES], + uint32_t src_word) const; + + /** Convert a logical address to physical address + * + * Some memories may have a mapping between the address supplied on the + * request and the physical address used to access the memory array (for + * example scrambled memories). By default logical and physical address are + * the same. + */ + virtual uint32_t ToPhysAddr(uint32_t logical_addr) const { + return logical_addr; + } }; #endif // OPENTITAN_HW_DV_VERILATOR_CPP_MEM_AREA_H_ diff --git a/vendor/lowrisc_ip/dv/verilator/cpp/scrambled_ecc32_mem_area.cc b/vendor/lowrisc_ip/dv/verilator/cpp/scrambled_ecc32_mem_area.cc new file mode 100644 index 00000000..87069a7a --- /dev/null +++ b/vendor/lowrisc_ip/dv/verilator/cpp/scrambled_ecc32_mem_area.cc @@ -0,0 +1,191 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +#include "scrambled_ecc32_mem_area.h" + +#include +#include +#include +#include + +#include "scramble_model.h" +#include "sv_scoped.h" + +// This is the maximum width of a nonce that's supported by the code in +// prim_util_get_scramble_key_nonce.svh +static const uint32_t kScrMaxNonceWidth = 320; +static const uint32_t kScrMaxNonceWidthByte = (kScrMaxNonceWidth + 7) / 8; + +// Functions to convert from integer address to/from a little-endian vector of +// bytes, addr_width is given in bits +static std::vector AddrIntToBytes(uint32_t addr, uint32_t addr_width) { + uint32_t addr_width_bytes = (addr_width + 7) / 8; + std::vector addr_bytes(addr_width_bytes); + + for (int i = 0; i < addr_width_bytes; ++i) { + addr_bytes[i] = addr & 0xff; + addr >>= 8; + } + + return addr_bytes; +} + +static uint32_t AddrBytesToInt(const std::vector &addr) { + assert(addr.size() <= 4); + + uint32_t addr_out = 0; + int cur_shift = 0; + + for (int i = 0; i < addr.size(); ++i) { + addr_out |= (addr[i] << cur_shift); + cur_shift += 8; + } + + return addr_out; +} + +// Converts svBitVecVal (bit[m:n] SV type) into a byte vector +static std::vector ByteVecFromSV(svBitVecVal sv_val[], + uint32_t bytes) { + int shift = 0; + std::vector vec(bytes); + for (int i = 0; i < bytes; ++i) { + vec[i] = (sv_val[i / 4] >> shift) & 0xff; + + shift += 8; + if (shift == 32) { + shift = 0; + } + } + + return vec; +} + +// Analogous to the vbits SystemVerilog function from prim_util_pkg.sv. It +// calculates the number of bits needed to address size items. +static uint32_t vbits(uint32_t size) { + assert(size > 0); + + if (size == 1) { + return 1; + } + + size -= 1; + uint32_t width = 0; + while (size) { + width++; + size /= 2; + } + + return width; +} + +extern "C" { +int simutil_get_scramble_key(svBitVecVal *key); +int simutil_get_scramble_nonce(svBitVecVal *nonce); +} + +std::vector ScrambledEcc32MemArea::GetScrambleKey() const { + SVScoped scoped(scr_scope_); + svBitVecVal key_minibuf[((kPrinceWidthByte * 2) + 3) / 4]; + + if (!simutil_get_scramble_key(key_minibuf)) { + std::ostringstream oss; + oss << "Could not read key at scope " << scr_scope_; + throw std::runtime_error(oss.str()); + } + + return ByteVecFromSV(key_minibuf, kPrinceWidthByte * 2); +} + +std::vector ScrambledEcc32MemArea::GetScrambleNonce() const { + assert(GetNonceWidthByte() <= kScrMaxNonceWidthByte); + + SVScoped scoped(scr_scope_); + svBitVecVal nonce_minibuf[(kScrMaxNonceWidthByte + 3) / 4]; + + if (!simutil_get_scramble_nonce((svBitVecVal *)nonce_minibuf)) { + std::ostringstream oss; + oss << "Could not read nonce at scope " << scr_scope_; + throw std::runtime_error(oss.str()); + } + + return ByteVecFromSV(nonce_minibuf, GetNonceWidthByte()); +} + +ScrambledEcc32MemArea::ScrambledEcc32MemArea(const std::string &scope, + uint32_t size, uint32_t width_32, + bool repeat_keystream) + : Ecc32MemArea( + SVScoped::join_sv_scopes( + scope, "u_prim_ram_1p_adv.u_mem.gen_generic.u_impl_generic"), + size, width_32), + scr_scope_(scope) { + addr_width_ = vbits(size); + repeat_keystream_ = repeat_keystream; +} + +uint32_t ScrambledEcc32MemArea::GetPhysWidth() const { + return (GetWidthByte() / 4) * 39; +} + +uint32_t ScrambledEcc32MemArea::GetPhysWidthByte() const { + return (GetPhysWidth() + 7) / 8; +} + +uint32_t ScrambledEcc32MemArea::GetPrinceReplications() const { + if (repeat_keystream_) { + return 1; + } else { + return (GetPhysWidthByte() + 7) / 8; + } +} + +uint32_t ScrambledEcc32MemArea::GetNonceWidth() const { + return GetPrinceReplications() * 64; +} + +uint32_t ScrambledEcc32MemArea::GetNonceWidthByte() const { + return GetPrinceReplications() * 8; +} + +void ScrambledEcc32MemArea::WriteBuffer(uint8_t buf[SV_MEM_WIDTH_BYTES], + const std::vector &data, + size_t start_idx, + uint32_t dst_word) const { + // Compute integrity + Ecc32MemArea::WriteBuffer(buf, data, start_idx, dst_word); + + std::vector scramble_buf = + std::vector(buf, buf + GetPhysWidthByte()); + + // Scramble data with integrity + scramble_buf = scramble_encrypt_data( + scramble_buf, GetPhysWidth(), 39, AddrIntToBytes(dst_word, addr_width_), + addr_width_, GetScrambleNonce(), GetScrambleKey(), repeat_keystream_); + + // Copy scrambled data to write buffer + std::copy(scramble_buf.begin(), scramble_buf.end(), &buf[0]); +} + +void ScrambledEcc32MemArea::ReadBuffer(std::vector &data, + const uint8_t buf[SV_MEM_WIDTH_BYTES], + uint32_t src_word) const { + // Unscramble data from read buffer + std::vector scrambled_data = + std::vector(buf, buf + GetPhysWidthByte()); + std::vector unscrambled_data = scramble_decrypt_data( + scrambled_data, GetPhysWidth(), 39, AddrIntToBytes(src_word, addr_width_), + addr_width_, GetScrambleNonce(), GetScrambleKey(), repeat_keystream_); + + // Strip integrity to give final result + Ecc32MemArea::ReadBuffer(data, &unscrambled_data[0], src_word); +} + +uint32_t ScrambledEcc32MemArea::ToPhysAddr(uint32_t logical_addr) const { + // Scramble logical address to get physical address + return AddrBytesToInt(scramble_addr(AddrIntToBytes(logical_addr, addr_width_), + addr_width_, GetScrambleNonce(), + GetNonceWidth())); +} diff --git a/vendor/lowrisc_ip/dv/verilator/cpp/scrambled_ecc32_mem_area.h b/vendor/lowrisc_ip/dv/verilator/cpp/scrambled_ecc32_mem_area.h new file mode 100644 index 00000000..98a5a1c7 --- /dev/null +++ b/vendor/lowrisc_ip/dv/verilator/cpp/scrambled_ecc32_mem_area.h @@ -0,0 +1,60 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +#ifndef OPENTITAN_HW_DV_VERILATOR_CPP_SCRAMBLED_ECC32_MEM_AREA_H_ +#define OPENTITAN_HW_DV_VERILATOR_CPP_SCRAMBLED_ECC32_MEM_AREA_H_ + +#include + +#include "ecc32_mem_area.h" + +/** + * A memory that implements scrambling over a 32-bit ECC integrity protection + * scheme storing 39 = 32 + 7 bits of physical data for each 32 bits of logical + * data. + */ +class ScrambledEcc32MemArea : public Ecc32MemArea { + public: + /** + * Constructor + * + * Create an ScrambledEcc32MemArea that will connect to a SystemVerilog memory + * at scope. It is size words long. Each memory word is 4 * width_32 bytes + * wide in the address space and 39 * width_32 bits wide in the physical + * memory. + * + * If the keystream of one single PRINCE instance should be repeated, + * set "repeat_keystream" to true. If this is set to false, multiple + * PRINCE instances are employed to produce the keystream. + * + */ + ScrambledEcc32MemArea(const std::string &scope, uint32_t size, + uint32_t width_32, bool repeat_keystream = true); + + private: + void WriteBuffer(uint8_t buf[SV_MEM_WIDTH_BYTES], + const std::vector &data, size_t start_idx, + uint32_t dst_word) const override; + + void ReadBuffer(std::vector &data, + const uint8_t buf[SV_MEM_WIDTH_BYTES], + uint32_t src_word) const override; + + uint32_t ToPhysAddr(uint32_t logical_addr) const override; + + uint32_t GetPhysWidth() const; + uint32_t GetPhysWidthByte() const; + uint32_t GetPrinceReplications() const; + uint32_t GetNonceWidth() const; + uint32_t GetNonceWidthByte() const; + + std::vector GetScrambleKey() const; + std::vector GetScrambleNonce() const; + + std::string scr_scope_; + uint32_t addr_width_; + bool repeat_keystream_; +}; + +#endif // OPENTITAN_HW_DV_VERILATOR_CPP_SCRAMBLED_ECC32_MEM_AREA_H_ diff --git a/vendor/lowrisc_ip/dv/verilator/cpp/sv_scoped.cc b/vendor/lowrisc_ip/dv/verilator/cpp/sv_scoped.cc index 102b0aa9..6f537ae8 100644 --- a/vendor/lowrisc_ip/dv/verilator/cpp/sv_scoped.cc +++ b/vendor/lowrisc_ip/dv/verilator/cpp/sv_scoped.cc @@ -93,3 +93,12 @@ SVScoped::Error::Error(const std::string &scope_name) oss << "No such SystemVerilog scope: `" << scope_name << "'."; msg_ = oss.str(); } + +std::string SVScoped::join_sv_scopes(const std::string &a, + const std::string &b) { + assert(a.size() && b.size()); + // If a = ".." and b = "foo.bar", we want "..foo.bar". Otherwise, a + // = "..foo" and b = "bar.baz", so we want "..foo.bar.baz" + // (inserting a "." between the two) + return (a.back() == '.') ? a + b : a + "." + b; +} diff --git a/vendor/lowrisc_ip/dv/verilator/cpp/sv_scoped.h b/vendor/lowrisc_ip/dv/verilator/cpp/sv_scoped.h index daa1247f..e9c75ff2 100644 --- a/vendor/lowrisc_ip/dv/verilator/cpp/sv_scoped.h +++ b/vendor/lowrisc_ip/dv/verilator/cpp/sv_scoped.h @@ -5,6 +5,7 @@ #ifndef OPENTITAN_HW_DV_VERILATOR_CPP_SV_SCOPED_H_ #define OPENTITAN_HW_DV_VERILATOR_CPP_SV_SCOPED_H_ +#include #include #include #include @@ -44,6 +45,9 @@ class SVScoped { std::string msg_; }; + // helper function to join two, possibly relative, scopes correctly. + static std::string join_sv_scopes(const std::string &a, const std::string &b); + private: svScope prev_scope_; }; diff --git a/vendor/lowrisc_ip/dv/verilator/memutil_dpi_scrambled.core b/vendor/lowrisc_ip/dv/verilator/memutil_dpi_scrambled.core new file mode 100644 index 00000000..c351caec --- /dev/null +++ b/vendor/lowrisc_ip/dv/verilator/memutil_dpi_scrambled.core @@ -0,0 +1,21 @@ +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_verilator:memutil_dpi_scrambled" +description: "DPI memory utilities for scrambled memories" +filesets: + files_cpp: + depend: + - lowrisc:dv_verilator:memutil_dpi + - lowrisc:dv:scramble_model + files: + - cpp/scrambled_ecc32_mem_area.cc + - cpp/scrambled_ecc32_mem_area.h: { is_include_file: true } + file_type: cppSource + +targets: + default: + filesets: + - files_cpp diff --git a/vendor/lowrisc_ip/dv/verilator/memutil_dpi_scrambled_opts.hjson b/vendor/lowrisc_ip/dv/verilator/memutil_dpi_scrambled_opts.hjson new file mode 100644 index 00000000..af87abdd --- /dev/null +++ b/vendor/lowrisc_ip/dv/verilator/memutil_dpi_scrambled_opts.hjson @@ -0,0 +1,44 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +{ + // Additional build-time options needed to compile C++ sources in + // simulators such as VCS and Xcelium for anything that uses + // memutil_dpi_scrambled. + memutil_dpi_core: "lowrisc:dv_verilator:memutil_dpi:0" + memutil_dpi_src_dir: "{eval_cmd} echo \"{memutil_dpi_core}\" | tr ':' '_'" + + memutil_dpi_scrambled_core: "lowrisc:dv_verilator:memutil_dpi_scrambled:0" + memutil_dpi_scrambled_src_dir: "{eval_cmd} echo \"{memutil_dpi_scrambled_core}\" | tr ':' '_'" + + secded_enc_core: "lowrisc:dv:secded_enc:0" + secded_enc_src_dir: "{eval_cmd} echo \"{secded_enc_core}\" | tr ':' '_'" + + scramble_model_core: "lowrisc:dv:scramble_model:0" + scramble_model_dir: "{eval_cmd} echo \"{scramble_model_core}\" | tr ':' '_'" + + prince_ref_core: "lowrisc:dv:crypto_prince_ref:0.1" + prince_ref_src_dir: "{eval_cmd} echo \"{prince_ref_core}\" | tr ':' '_'" + + + build_modes: [ + { + name: vcs_memutil_dpi_scrambled_build_opts + build_opts: ["-CFLAGS -I{build_dir}/src/{memutil_dpi_src_dir}/cpp", + "-CFLAGS -I{build_dir}/src/{memutil_dpi_scrambled_src_dir}/cpp", + "-CFLAGS -I{build_dir}/src/{secded_enc_src_dir}", + "-CFLAGS -I{build_dir}/src/{scramble_model_dir}", + "-CFLAGS -I{build_dir}/src/{prince_ref_src_dir}", + "-lelf"] + } + + { + name: xcelium_memutil_dpi_scrambled_build_opts + build_opts: ["-I{build_dir}/src/{memutil_dpi_src_dir}/cpp", + "-I{build_dir}/src/{memutil_dpi_scrambled_src_dir}/cpp", + "-I{build_dir}/src/{prince_ref_src_dir}", + "-I{build_dir}/src/{scramble_model_dir}", + "-lelf"] + } + ] +} diff --git a/vendor/lowrisc_ip/dv/verilator/simutil_verilator/cpp/verilator_sim_ctrl.cc b/vendor/lowrisc_ip/dv/verilator/simutil_verilator/cpp/verilator_sim_ctrl.cc index 0721d7cd..33816dac 100644 --- a/vendor/lowrisc_ip/dv/verilator/simutil_verilator/cpp/verilator_sim_ctrl.cc +++ b/vendor/lowrisc_ip/dv/verilator/simutil_verilator/cpp/verilator_sim_ctrl.cc @@ -60,6 +60,52 @@ std::pair VerilatorSimCtrl::Exec(int argc, char **argv) { return std::make_pair(retcode, true); } +static bool read_ul_arg(unsigned long *arg_val, const char *arg_name, + const char *arg_text) { + assert(arg_val && arg_name && arg_text); + + bool bad_fmt = false; + bool out_of_range = false; + + // We have a stricter input format that strtoul: no leading space and no + // leading plus or minus signs (strtoul has magic behaviour if the input + // starts with a minus sign, but we don't want that). We're using auto base + // detection, but a valid number will always start with 0-9 (since hex + // constants start with "0x") + if (!(('0' <= arg_text[0]) && (arg_text[0] <= '9'))) { + bad_fmt = true; + } else { + char *txt_end; + *arg_val = strtoul(arg_text, &txt_end, 0); + + // If txt_end doesn't point at a \0 then we didn't read the entire + // argument. + if (*txt_end) { + bad_fmt = true; + } else { + // If the value was too big to fit in an unsigned long, strtoul sets + // errno to ERANGE. + if (errno != 0) { + assert(errno == ERANGE); + out_of_range = true; + } + } + } + + if (bad_fmt) { + std::cerr << "ERROR: Bad format for " << arg_name << " argument: `" + << arg_text << "' is not an unsigned integer.\n"; + return false; + } + if (out_of_range) { + std::cerr << "ERROR: Bad format for " << arg_name << " argument: `" + << arg_text << "' is too big.\n"; + return false; + } + + return true; +} + bool VerilatorSimCtrl::ParseCommandArgs(int argc, char **argv, bool &exit_app) { const struct option long_options[] = { {"term-after-cycles", required_argument, nullptr, 'c'}, @@ -89,7 +135,10 @@ bool VerilatorSimCtrl::ParseCommandArgs(int argc, char **argv, bool &exit_app) { TraceOn(); break; case 'c': - term_after_cycles_ = atoi(optarg); + if (!read_ul_arg(&term_after_cycles_, "term-after-cycles", optarg)) { + exit_app = true; + return false; + } break; case 'h': PrintHelp(); @@ -159,6 +208,10 @@ void VerilatorSimCtrl::SetResetDuration(unsigned int cycles) { reset_duration_cycles_ = cycles; } +void VerilatorSimCtrl::SetTimeout(unsigned int cycles) { + term_after_cycles_ = cycles; +} + void VerilatorSimCtrl::RequestStop(bool simulation_success) { request_stop_ = true; simulation_success_ &= simulation_success; @@ -217,7 +270,7 @@ void VerilatorSimCtrl::PrintHelp() const { " Write a trace file from the start\n\n"; } std::cout << "-c|--term-after-cycles=N\n" - " Terminate simulation after N cycles\n\n" + " Terminate simulation after N cycles. 0 means no timeout.\n\n" "-h|--help\n" " Show help\n\n" "All arguments are passed to the design and can be used " diff --git a/vendor/lowrisc_ip/dv/verilator/simutil_verilator/cpp/verilator_sim_ctrl.h b/vendor/lowrisc_ip/dv/verilator/simutil_verilator/cpp/verilator_sim_ctrl.h index 58098cea..f1bc1b16 100644 --- a/vendor/lowrisc_ip/dv/verilator/simutil_verilator/cpp/verilator_sim_ctrl.h +++ b/vendor/lowrisc_ip/dv/verilator/simutil_verilator/cpp/verilator_sim_ctrl.h @@ -97,6 +97,15 @@ class VerilatorSimCtrl { */ void SetResetDuration(unsigned int cycles); + /** + * Set a timeout in clock cycles. + * + * This can be overridden by the user (in either direction) with the + * --term-after-cycles command-line argument. Setting to zero means + * no timeout, which is the default behaviour. + */ + void SetTimeout(unsigned int cycles); + /** * Request the simulation to stop */ @@ -129,7 +138,7 @@ class VerilatorSimCtrl { std::chrono::steady_clock::time_point time_begin_; std::chrono::steady_clock::time_point time_end_; VerilatedTracer tracer_; - int term_after_cycles_; + unsigned long term_after_cycles_; std::vector extension_array_; /** diff --git a/vendor/lowrisc_ip/ip/prim/dv/prim_prince/crypto_dpi_prince/crypto_dpi_prince.core b/vendor/lowrisc_ip/ip/prim/dv/prim_prince/crypto_dpi_prince/crypto_dpi_prince.core index 4d83d81b..1ce81335 100644 --- a/vendor/lowrisc_ip/ip/prim/dv/prim_prince/crypto_dpi_prince/crypto_dpi_prince.core +++ b/vendor/lowrisc_ip/ip/prim/dv/prim_prince/crypto_dpi_prince/crypto_dpi_prince.core @@ -3,11 +3,12 @@ CAPI=2: # Licensed under the Apache License, Version 2.0, see LICENSE for details. # SPDX-License-Identifier: Apache-2.0 name: "lowrisc:dv:crypto_dpi_prince:0.1" -description: "PRINCE block cipher reference C implementation from Sebastien Riou" +description: "PRINCE block cipher reference C implementation DPI interface" filesets: files_dv: + depend: + - lowrisc:dv:crypto_prince_ref files: - - prince_ref.h: {file_type: cSource, is_include_file: true} - crypto_dpi_prince.c: {file_type: cSource} - crypto_dpi_prince_pkg.sv: {file_type: systemVerilogSource} diff --git a/vendor/lowrisc_ip/ip/prim/dv/prim_prince/crypto_dpi_prince/crypto_dpi_prince_sim_opts.hjson b/vendor/lowrisc_ip/ip/prim/dv/prim_prince/crypto_dpi_prince/crypto_dpi_prince_sim_opts.hjson new file mode 100644 index 00000000..8c5b0f7c --- /dev/null +++ b/vendor/lowrisc_ip/ip/prim/dv/prim_prince/crypto_dpi_prince/crypto_dpi_prince_sim_opts.hjson @@ -0,0 +1,22 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +{ + // Additional build-time options needed to compile C++ sources in + // simulators such as VCS and Xcelium for anything that uses + // crypto_prince_ref. + crypto_prince_ref_core: "lowrisc:dv:crypto_prince_ref:0.1" + crypto_prince_ref_src_dir: "{eval_cmd} echo \"{crypto_prince_ref_core}\" | tr ':' '_'" + + build_modes: [ + { + name: vcs_crypto_dpi_prince_build_opts + build_opts: ["-CFLAGS -I{build_dir}/src/{crypto_prince_ref_src_dir}"] + } + + { + name: xcelium_crypto_dpi_prince_build_opts + build_opts: ["-I{build_dir}/src/{crypto_prince_ref_src_dir}"] + } + ] +} diff --git a/vendor/lowrisc_ip/ip/prim/dv/prim_prince/crypto_dpi_prince/crypto_prince_ref.core b/vendor/lowrisc_ip/ip/prim/dv/prim_prince/crypto_dpi_prince/crypto_prince_ref.core new file mode 100644 index 00000000..0ebd2a40 --- /dev/null +++ b/vendor/lowrisc_ip/ip/prim/dv/prim_prince/crypto_dpi_prince/crypto_prince_ref.core @@ -0,0 +1,15 @@ +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:crypto_prince_ref:0.1" +description: "PRINCE block cipher reference C implementation from Sebastien Riou" +filesets: + files_dv: + files: + - prince_ref.h: {file_type: cSource, is_include_file: true} + +targets: + default: + filesets: + - files_dv diff --git a/vendor/lowrisc_ip/ip/prim/dv/prim_prince/prim_prince_sim_cfg.hjson b/vendor/lowrisc_ip/ip/prim/dv/prim_prince/prim_prince_sim_cfg.hjson index 755e52d2..46a3d19a 100644 --- a/vendor/lowrisc_ip/ip/prim/dv/prim_prince/prim_prince_sim_cfg.hjson +++ b/vendor/lowrisc_ip/ip/prim/dv/prim_prince/prim_prince_sim_cfg.hjson @@ -18,7 +18,11 @@ fusesoc_core: lowrisc:dv:prim_prince_sim:0.1 // Import additional common sim cfg files. - import_cfgs: ["{proj_root}/hw/dv/tools/dvsim/common_sim_cfg.hjson"] + import_cfgs: ["{proj_root}/hw/dv/tools/dvsim/common_sim_cfg.hjson", + // Config files to get the correct flags for crypto_dpi_prince + "{proj_root}/hw/ip/prim/dv/prim_prince/crypto_dpi_prince/crypto_dpi_prince_sim_opts.hjson"] + + en_build_modes: ["{tool}_crypto_dpi_prince_build_opts"] // Default iterations for all tests - each test entry can override this. reseed: 50 diff --git a/vendor/lowrisc_ip/ip/prim/dv/prim_ram_scr/cpp/scramble_model.cc b/vendor/lowrisc_ip/ip/prim/dv/prim_ram_scr/cpp/scramble_model.cc new file mode 100644 index 00000000..04cc5887 --- /dev/null +++ b/vendor/lowrisc_ip/ip/prim/dv/prim_ram_scr/cpp/scramble_model.cc @@ -0,0 +1,362 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +#include "scramble_model.h" + +#include +#include +#include +#include +#include +#include + +#include "prince_ref.h" + +uint8_t PRESENT_SBOX4[] = {0xc, 0x5, 0x6, 0xb, 0x9, 0x0, 0xa, 0xd, + 0x3, 0xe, 0xf, 0x8, 0x4, 0x7, 0x1, 0x2}; + +uint8_t PRESENT_SBOX4_INV[] = {0x5, 0xe, 0xf, 0x8, 0xc, 0x1, 0x2, 0xd, + 0xb, 0x4, 0x6, 0x3, 0x0, 0x7, 0x9, 0xa}; + +static const uint32_t kNumAddrSubstPermRounds = 2; +static const uint32_t kNumDataSubstPermRounds = 2; +static const uint32_t kNumPrinceHalfRounds = 2; + +static std::vector byte_reverse_vector( + const std::vector &vec_in) { + std::vector vec_out(vec_in.size()); + + std::reverse_copy(std::begin(vec_in), std::end(vec_in), std::begin(vec_out)); + + return vec_out; +} + +static uint8_t read_vector_bit(const std::vector &vec, + uint32_t bit_pos) { + assert(bit_pos / 8 < vec.size()); + + return (vec[bit_pos / 8] >> (bit_pos % 8)) & 1; +} + +static void or_vector_bit(std::vector &vec, uint32_t bit_pos, + uint8_t bit) { + assert(bit_pos / 8 < vec.size()); + + vec[bit_pos / 8] |= bit << (bit_pos % 8); +} + +static std::vector xor_vectors(const std::vector &vec_a, + const std::vector &vec_b) { + assert(vec_a.size() == vec_b.size()); + + std::vector vec_out(vec_a.size()); + + std::transform(vec_a.begin(), vec_a.end(), vec_b.begin(), vec_out.begin(), + std::bit_xor{}); + + return vec_out; +} + +// Run each 4-bit chunk of bytes from `in` through the SBOX. Where `bit_width` +// isn't a multiple of 4 the remaining bits are just copied straight through. +// `invert` choose whether to use the inverted SBOX or not. +static std::vector scramble_sbox_layer(const std::vector &in, + uint32_t bit_width, + uint8_t sbox[16]) { + assert(in.size() == ((bit_width + 7) / 8)); + std::vector out(in.size(), 0); + + // Iterate through each 4 bit chunk of the data and apply the appropriate SBOX + for (int i = 0; i < bit_width / 4; ++i) { + uint8_t sbox_in, sbox_out; + + sbox_in = in[i / 2]; + + int shift = (i % 2) ? 4 : 0; + sbox_in = (sbox_in >> shift) & 0xf; + + sbox_out = sbox[sbox_in]; + + out[i / 2] |= sbox_out << shift; + } + + // Where bit_width is not a multiple of 4 copy over the remaining bits + if (bit_width % 4) { + int shift = ((bit_width % 8) >= 4) ? 4 : 0; + uint8_t nibble = (in[bit_width / 8] >> shift) & 0xf; + out[bit_width / 8] |= nibble << shift; + } + + return out; +} + +// Reverse bits from incoming byte vector +static std::vector scramble_flip_layer(const std::vector &in, + uint32_t bit_width) { + assert(in.size() == ((bit_width + 7) / 8)); + std::vector out(in.size(), 0); + + for (int i = 0; i < bit_width; ++i) { + or_vector_bit(out, bit_width - i - 1, read_vector_bit(in, i)); + } + + return out; +} + +// Apply butterfly to incoming byte vector. Even bits are placed in the lower +// half of the output, odd bits are placed in the upper half of the output. +static std::vector scramble_perm_layer(const std::vector &in, + uint32_t bit_width, + bool invert) { + assert(in.size() == ((bit_width + 7) / 8)); + std::vector out(in.size(), 0); + + for (int i = 0; i < bit_width / 2; ++i) { + if (invert) { + or_vector_bit(out, i * 2, read_vector_bit(in, i)); + or_vector_bit(out, i * 2 + 1, read_vector_bit(in, i + (bit_width / 2))); + } else { + or_vector_bit(out, i, read_vector_bit(in, i * 2)); + or_vector_bit(out, i + (bit_width / 2), read_vector_bit(in, i * 2 + 1)); + } + } + + if (bit_width % 2) { + // Where bit_width isn't even, the final bit is copied across to the same + // position + or_vector_bit(out, bit_width - 1, read_vector_bit(in, bit_width - 1)); + } + + return out; +} + +// Apply a full set of subsitution/permutation rounds for encrypt to the +// incoming byte vector +static std::vector scramble_subst_perm_enc( + const std::vector &in, const std::vector &key, + uint32_t bit_width, uint32_t num_rounds) { + assert(in.size() == ((bit_width + 7) / 8)); + assert(key.size() == ((bit_width + 7) / 8)); + + std::vector state(in); + + for (int i = 0; i < num_rounds; ++i) { + state = xor_vectors(state, key); + + state = scramble_sbox_layer(state, bit_width, PRESENT_SBOX4); + state = scramble_flip_layer(state, bit_width); + state = scramble_perm_layer(state, bit_width, false); + } + + state = xor_vectors(state, key); + + return state; +} + +// Apply a full set of substitution/permutation rounds for decrypt to the +// incoming byte vector +static std::vector scramble_subst_perm_dec( + const std::vector &in, const std::vector &key, + uint32_t bit_width, uint32_t num_rounds) { + assert(in.size() == ((bit_width + 7) / 8)); + assert(key.size() == ((bit_width + 7) / 8)); + + std::vector state(in); + + for (int i = 0; i < num_rounds; ++i) { + state = xor_vectors(state, key); + + state = scramble_perm_layer(state, bit_width, true); + state = scramble_flip_layer(state, bit_width); + state = scramble_sbox_layer(state, bit_width, PRESENT_SBOX4_INV); + } + + state = xor_vectors(state, key); + + return state; +} + +// Generate a keystream for XORing with data using PRINCE. +// If repeat_keystream is set to true, the output from one PRINCE instance is +// repeated when the keystream is greater than a single PRINCE width (64bit). +// Otherwise, multiple PRINCEs are instantiated to form the keystream. +static std::vector scramble_gen_keystream( + const std::vector &addr, uint32_t addr_width, + const std::vector &nonce, const std::vector &key, + uint32_t keystream_width, uint32_t num_half_rounds, bool repeat_keystream) { + assert(key.size() == (kPrinceWidthByte * 2)); + + // Determine how many PRINCE replications are required + uint32_t num_princes, num_repetitions; + if (repeat_keystream) { + num_princes = 1; + num_repetitions = (keystream_width + kPrinceWidth - 1) / kPrinceWidth; + } else { + num_princes = (keystream_width + kPrinceWidth - 1) / kPrinceWidth; + num_repetitions = 1; + } + + std::vector keystream; + + for (int i = 0; i < num_princes; ++i) { + // Initial vector is data for PRINCE to encrypt. Formed from nonce and data + // address + std::vector iv(8, 0); + + for (int j = 0; j < kPrinceWidth; ++j) { + if (j < addr_width) { + // Bottom addr_width bits of IV are address + or_vector_bit(iv, j, read_vector_bit(addr, j)); + } else { + // Other bits are taken from nonce. Each PRINCE instantiation will use + // different nonce bits. + int nonce_bit = (j - addr_width) + i * (kPrinceWidth - addr_width); + or_vector_bit(iv, j, read_vector_bit(nonce, nonce_bit)); + } + } + + // PRINCE C reference model works on big-endian byte order + iv = byte_reverse_vector(iv); + auto key_be = byte_reverse_vector(key); + + // Apply PRINCE to IV to produce keystream + std::vector keystream_block(kPrinceWidthByte); + prince_enc_dec(&iv[0], &key_be[0], &keystream_block[0], 0, num_half_rounds, + 0); + + // Flip keystream into little endian order and add to keystream vector + keystream_block = byte_reverse_vector(keystream_block); + // Repeat the output of a single PRINCE instance if needed + for (int k = 0; k < num_repetitions; ++k) { + keystream.insert(keystream.end(), keystream_block.begin(), + keystream_block.end()); + } + } + + // Total keystream bits generated are some multiple of kPrinceWidth. This can + // result in unused keystream bits. Remove the unused bytes from the keystream + // vector and zero out top unused bits in the final byte if required. + uint32_t keystream_bytes = (keystream_width + 7) / 8; + uint32_t keystream_bytes_to_erase = keystream.size() - keystream_bytes; + if (keystream_bytes_to_erase) { + keystream.erase(keystream.end() - keystream_bytes_to_erase, + keystream.end()); + } + + if (keystream_width % 8) { + keystream[keystream.size() - 1] &= (1 << (keystream_width % 8)) - 1; + } + + return keystream; +} + +// Split incoming data into subst_perm_width chunks and individually apply the +// substitution/permutation layer to each +static std::vector scramble_subst_perm_full_width( + const std::vector &in, uint32_t bit_width, + uint32_t subst_perm_width, bool enc) { + assert(in.size() == ((bit_width + 7) / 8)); + + // Determine how many bytes each subst_perm_width chunk is and how many + // chunks are needed to cover the full bit_width. + uint32_t subst_perm_bytes = (subst_perm_width + 7) / 8; + uint32_t subst_perm_blocks = + (bit_width + subst_perm_width - 1) / subst_perm_width; + + std::vector out(in.size(), 0); + std::vector zero_key(subst_perm_bytes, 0); + + auto sp_scrambler = enc ? scramble_subst_perm_enc : scramble_subst_perm_dec; + + for (int i = 0; i < subst_perm_blocks; ++i) { + // Where bit_width does not evenly divide into subst_perm_width the + // final block is smaller. + uint32_t bits_so_far = subst_perm_width * i; + uint32_t block_width = std::min(subst_perm_width, bit_width - bits_so_far); + + std::vector subst_perm_data(subst_perm_bytes, 0); + + // Extract bits from in for this chunk + for (int j = 0; j < block_width; ++j) { + or_vector_bit(subst_perm_data, j, + read_vector_bit(in, j + i * subst_perm_width)); + } + + // Apply the substitution/permutation layer to the chunk + auto subst_perm_out = sp_scrambler(subst_perm_data, zero_key, block_width, + kNumDataSubstPermRounds); + + // Write the result to the `out` vector + for (int j = 0; j < block_width; ++j) { + or_vector_bit(out, j + i * subst_perm_width, + read_vector_bit(subst_perm_out, j)); + } + } + + return out; +} + +std::vector scramble_addr(const std::vector &addr_in, + uint32_t addr_width, + const std::vector &nonce, + uint32_t nonce_width) { + assert(addr_in.size() == ((addr_width + 7) / 8)); + + std::vector addr_enc_nonce(addr_in.size(), 0); + + // Address is scrambled by using substitution/permutation layer with the nonce + // used as a key. + // Extract relevant nonce bits for key + for (int i = 0; i < addr_width; ++i) { + or_vector_bit(addr_enc_nonce, i, + read_vector_bit(nonce, nonce_width - addr_width + i)); + } + + // Apply substitution/permutation layer + return scramble_subst_perm_enc(addr_in, addr_enc_nonce, addr_width, + kNumAddrSubstPermRounds); +} + +std::vector scramble_encrypt_data( + const std::vector &data_in, uint32_t data_width, + uint32_t subst_perm_width, const std::vector &addr, + uint32_t addr_width, const std::vector &nonce, + const std::vector &key, bool repeat_keystream) { + assert(data_in.size() == ((data_width + 7) / 8)); + assert(addr.size() == ((addr_width + 7) / 8)); + + // Data is encrypted by XORing with keystream then applying + // substitution/permutation layer + + auto keystream = + scramble_gen_keystream(addr, addr_width, nonce, key, data_width, + kNumPrinceHalfRounds, repeat_keystream); + + auto data_enc = xor_vectors(data_in, keystream); + + return scramble_subst_perm_full_width(data_enc, data_width, subst_perm_width, + true); +} + +std::vector scramble_decrypt_data( + const std::vector &data_in, uint32_t data_width, + uint32_t subst_perm_width, const std::vector &addr, + uint32_t addr_width, const std::vector &nonce, + const std::vector &key, bool repeat_keystream) { + assert(data_in.size() == ((data_width + 7) / 8)); + assert(addr.size() == ((addr_width + 7) / 8)); + + // Data is decrypted by reversing substitution/permutation layer then XORing + // with keystream + auto data_sp_out = scramble_subst_perm_full_width(data_in, data_width, + subst_perm_width, false); + + auto keystream = + scramble_gen_keystream(addr, addr_width, nonce, key, data_width, + kNumPrinceHalfRounds, repeat_keystream); + + auto data_dec = xor_vectors(data_sp_out, keystream); + + return data_dec; +} diff --git a/vendor/lowrisc_ip/ip/prim/dv/prim_ram_scr/cpp/scramble_model.core b/vendor/lowrisc_ip/ip/prim/dv/prim_ram_scr/cpp/scramble_model.core new file mode 100644 index 00000000..af0e223a --- /dev/null +++ b/vendor/lowrisc_ip/ip/prim/dv/prim_ram_scr/cpp/scramble_model.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:dv:scramble_model" +description: "Memory scrambling C++ model" +filesets: + files_cpp: + depend: + - lowrisc:dv:crypto_prince_ref + files: + - scramble_model.cc + - scramble_model.h: { is_include_file: true } + file_type: cppSource + +targets: + default: + filesets: + - files_cpp diff --git a/vendor/lowrisc_ip/ip/prim/dv/prim_ram_scr/cpp/scramble_model.h b/vendor/lowrisc_ip/ip/prim/dv/prim_ram_scr/cpp/scramble_model.h new file mode 100644 index 00000000..4a3875d2 --- /dev/null +++ b/vendor/lowrisc_ip/ip/prim/dv/prim_ram_scr/cpp/scramble_model.h @@ -0,0 +1,71 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +#ifndef OPENTITAN_HW_IP_PRIM_DV_PRIM_RAM_SCR_CPP_SCRAMBLE_MODEL_H_ +#define OPENTITAN_HW_IP_PRIM_DV_PRIM_RAM_SCR_CPP_SCRAMBLE_MODEL_H_ + +#include +#include + +const uint32_t kPrinceWidth = 64; +const uint32_t kPrinceWidthByte = kPrinceWidth / 8; + +// C++ model of memory scrambling. All byte vectors are in little endian byte +// order (least significant byte at index 0). + +/** Scramble an address to give the physical address used to access the + * scrambled memory. Return vector of scrambled address bytes + * + * @param addr_in Byte vector of address + * @param addr_width Width of the address in bits + * @param nonce Byte vector of scrambling nonce + * @param nonce_width Width of scramble nonce in bits + * @return Byte vector with scrambled address + */ +std::vector scramble_addr(const std::vector &addr_in, + uint32_t addr_width, + const std::vector &nonce, + uint32_t nonce_width); + +/** Decrypt scrambled data + * @param data_in Byte vector of data to decrypt + * @param data_width Width of data in bits + * @param subst_perm_width Width over which the substitution/permutation network + * is applied (DiffWidth parameter on prim_ram_1p_scr) + * @param addr Byte vector of data address + * @param addr_width Width of the address in bits + * @param nonce Byte vector of scrambling nonce + * @param key Byte vector of scrambling key + * @param repeat_keystream Repeat the keystream of one single PRINCE instance if + * set to true. Otherwise multiple PRINCE instances are + * used. + * @return Byte vector with decrypted data + */ +std::vector scramble_decrypt_data( + const std::vector &data_in, uint32_t data_width, + uint32_t subst_perm_width, const std::vector &addr, + uint32_t addr_width, const std::vector &nonce, + const std::vector &key, bool repeat_keystream); + +/** Encrypt scrambled data + * @param data_in Byte vector of data to encrypt + * @param data_width Width of data in bits + * @param subst_perm_width Width over which the substitution/permutation network + * is applied (DiffWidth parameter on prim_ram_1p_scr) + * @param addr Byte vector of data address + * @param addr_width Width of the address in bits + * @param nonce Byte vector of scrambling nonce + * @param key Byte vector of scrambling key + * @param repeat_keystream Repeat the keystream of one single PRINCE instance if + * set to true. Otherwise multiple PRINCE instances are + * used. + * @return Byte vector with encrypted data + */ +std::vector scramble_encrypt_data( + const std::vector &data_in, uint32_t data_width, + uint32_t subst_perm_width, const std::vector &addr, + uint32_t addr_width, const std::vector &nonce, + const std::vector &key, bool repeat_keystream); + +#endif // OPENTITAN_HW_IP_PRIM_DV_PRIM_RAM_SCR_CPP_SCRAMBLE_MODEL_H_ diff --git a/vendor/lowrisc_ip/ip/prim/fpv/prim_alert_rxtx_async_fatal_fpv.core b/vendor/lowrisc_ip/ip/prim/fpv/prim_alert_rxtx_async_fatal_fpv.core index 8a8a3492..e9c5fdf1 100644 --- a/vendor/lowrisc_ip/ip/prim/fpv/prim_alert_rxtx_async_fatal_fpv.core +++ b/vendor/lowrisc_ip/ip/prim/fpv/prim_alert_rxtx_async_fatal_fpv.core @@ -8,6 +8,7 @@ filesets: files_formal: depend: - lowrisc:prim:all + - lowrisc:prim:alert files: - vip/prim_alert_rxtx_async_assert_fpv.sv - tb/prim_alert_rxtx_async_fatal_fpv.sv diff --git a/vendor/lowrisc_ip/ip/prim/fpv/prim_alert_rxtx_async_fpv.core b/vendor/lowrisc_ip/ip/prim/fpv/prim_alert_rxtx_async_fpv.core index 36569b6a..77c630da 100644 --- a/vendor/lowrisc_ip/ip/prim/fpv/prim_alert_rxtx_async_fpv.core +++ b/vendor/lowrisc_ip/ip/prim/fpv/prim_alert_rxtx_async_fpv.core @@ -8,6 +8,7 @@ filesets: files_formal: depend: - lowrisc:prim:all + - lowrisc:prim:alert files: - vip/prim_alert_rxtx_async_assert_fpv.sv - tb/prim_alert_rxtx_async_fpv.sv diff --git a/vendor/lowrisc_ip/ip/prim/fpv/prim_alert_rxtx_fatal_fpv.core b/vendor/lowrisc_ip/ip/prim/fpv/prim_alert_rxtx_fatal_fpv.core index 23758f37..bcab4445 100644 --- a/vendor/lowrisc_ip/ip/prim/fpv/prim_alert_rxtx_fatal_fpv.core +++ b/vendor/lowrisc_ip/ip/prim/fpv/prim_alert_rxtx_fatal_fpv.core @@ -8,6 +8,7 @@ filesets: files_formal: depend: - lowrisc:prim:all + - lowrisc:prim:alert files: - vip/prim_alert_rxtx_assert_fpv.sv - tb/prim_alert_rxtx_fatal_fpv.sv diff --git a/vendor/lowrisc_ip/ip/prim/fpv/prim_alert_rxtx_fpv.core b/vendor/lowrisc_ip/ip/prim/fpv/prim_alert_rxtx_fpv.core index 55b36fc4..65e9c9e7 100644 --- a/vendor/lowrisc_ip/ip/prim/fpv/prim_alert_rxtx_fpv.core +++ b/vendor/lowrisc_ip/ip/prim/fpv/prim_alert_rxtx_fpv.core @@ -8,6 +8,7 @@ filesets: files_formal: depend: - lowrisc:prim:all + - lowrisc:prim:alert files: - vip/prim_alert_rxtx_assert_fpv.sv - tb/prim_alert_rxtx_fpv.sv diff --git a/vendor/lowrisc_ip/ip/prim/fpv/prim_esc_rxtx_fpv.core b/vendor/lowrisc_ip/ip/prim/fpv/prim_esc_rxtx_fpv.core index 3aa4c5a7..31f422d1 100644 --- a/vendor/lowrisc_ip/ip/prim/fpv/prim_esc_rxtx_fpv.core +++ b/vendor/lowrisc_ip/ip/prim/fpv/prim_esc_rxtx_fpv.core @@ -8,6 +8,7 @@ filesets: files_formal: depend: - lowrisc:prim:all + - lowrisc:prim:esc files: - vip/prim_esc_rxtx_assert_fpv.sv - tb/prim_esc_rxtx_bind_fpv.sv diff --git a/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_alert_rxtx_async_fatal_fpv.sv b/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_alert_rxtx_async_fatal_fpv.sv index 18408c6e..fb7aa879 100644 --- a/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_alert_rxtx_async_fatal_fpv.sv +++ b/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_alert_rxtx_async_fatal_fpv.sv @@ -7,7 +7,6 @@ module prim_alert_rxtx_async_fatal_fpv import prim_alert_pkg::*; - import prim_esc_pkg::*; ( input clk_i, input rst_ni, diff --git a/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_alert_rxtx_async_fpv.sv b/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_alert_rxtx_async_fpv.sv index 682acaa4..30a1b105 100644 --- a/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_alert_rxtx_async_fpv.sv +++ b/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_alert_rxtx_async_fpv.sv @@ -7,7 +7,6 @@ module prim_alert_rxtx_async_fpv import prim_alert_pkg::*; - import prim_esc_pkg::*; ( input clk_i, input rst_ni, diff --git a/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_alert_rxtx_fatal_fpv.sv b/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_alert_rxtx_fatal_fpv.sv index 2eeb2463..e32e4e89 100644 --- a/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_alert_rxtx_fatal_fpv.sv +++ b/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_alert_rxtx_fatal_fpv.sv @@ -7,7 +7,6 @@ module prim_alert_rxtx_fatal_fpv import prim_alert_pkg::*; - import prim_esc_pkg::*; ( input clk_i, input rst_ni, diff --git a/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_alert_rxtx_fpv.sv b/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_alert_rxtx_fpv.sv index f89a0c21..f029e2e1 100644 --- a/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_alert_rxtx_fpv.sv +++ b/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_alert_rxtx_fpv.sv @@ -7,7 +7,6 @@ module prim_alert_rxtx_fpv import prim_alert_pkg::*; - import prim_esc_pkg::*; ( input clk_i, input rst_ni, diff --git a/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_arbiter_ppc_fpv.sv b/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_arbiter_ppc_fpv.sv index 7e7e38af..a5719921 100644 --- a/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_arbiter_ppc_fpv.sv +++ b/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_arbiter_ppc_fpv.sv @@ -9,12 +9,13 @@ module prim_arbiter_ppc_fpv #( parameter int unsigned N = 8, parameter int unsigned DW = 32, parameter bit EnDataPort = 1, - parameter bit EnReqStabA = 1, localparam int IdxW = $clog2(N) ) ( input clk_i, input rst_ni, + input req_chk_i, + input [ N-1:0] req_i, input [DW-1:0] data_i [N], output logic [ N-1:0] gnt_o, @@ -29,11 +30,11 @@ module prim_arbiter_ppc_fpv #( prim_arbiter_ppc #( .N(N), .DW(DW), - .EnDataPort(EnDataPort), - .EnReqStabA(EnReqStabA) + .EnDataPort(EnDataPort) ) i_prim_arbiter_ppc ( .clk_i, .rst_ni, + .req_chk_i, .req_i, .data_i, .gnt_o, diff --git a/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_arbiter_tree_fpv.sv b/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_arbiter_tree_fpv.sv index aa8c2992..b5a61c2b 100644 --- a/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_arbiter_tree_fpv.sv +++ b/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_arbiter_tree_fpv.sv @@ -9,12 +9,13 @@ module prim_arbiter_tree_fpv #( parameter int N = 8, parameter int DW = 32, parameter bit EnDataPort = 1, - parameter bit EnReqStabA = 1, localparam int IdxW = $clog2(N) ) ( input clk_i, input rst_ni, + input req_chk_i, + input [ N-1:0] req_i, input [DW-1:0] data_i [N], output logic [ N-1:0] gnt_o, @@ -29,11 +30,11 @@ module prim_arbiter_tree_fpv #( prim_arbiter_tree #( .N(N), .DW(DW), - .EnDataPort(EnDataPort), - .EnReqStabA(EnReqStabA) + .EnDataPort(EnDataPort) ) i_prim_arbiter_tree ( .clk_i, .rst_ni, + .req_chk_i, .req_i, .data_i, .gnt_o, diff --git a/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_esc_rxtx_fpv.sv b/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_esc_rxtx_fpv.sv index 11a9972f..df9999b1 100644 --- a/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_esc_rxtx_fpv.sv +++ b/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_esc_rxtx_fpv.sv @@ -6,7 +6,6 @@ // a formal tool. module prim_esc_rxtx_fpv - import prim_alert_pkg::*; import prim_esc_pkg::*; ( input clk_i, @@ -32,7 +31,7 @@ module prim_esc_rxtx_fpv assign esc_tx_in.esc_p = esc_tx_out.esc_p ^ esc_err_pi; assign esc_tx_in.esc_n = esc_tx_out.esc_n ^ esc_err_ni; - prim_esc_sender i_prim_esc_sender ( + prim_esc_sender u_prim_esc_sender ( .clk_i , .rst_ni , .ping_req_i , @@ -43,7 +42,11 @@ module prim_esc_rxtx_fpv .esc_tx_o ( esc_tx_out ) ); - prim_esc_receiver i_prim_esc_receiver ( + prim_esc_receiver #( + // This reduces the state space for this counter + // from 2**24 to 2**6 to speed up convergence. + .TimeoutCntDw(6) + ) u_prim_esc_receiver ( .clk_i , .rst_ni , .esc_en_o , diff --git a/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_secded_22_16_bind_fpv.sv b/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_secded_22_16_bind_fpv.sv index 4068a4f7..096d3d1d 100644 --- a/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_secded_22_16_bind_fpv.sv +++ b/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_secded_22_16_bind_fpv.sv @@ -10,8 +10,8 @@ module prim_secded_22_16_bind_fpv; prim_secded_22_16_assert_fpv prim_secded_22_16_assert_fpv ( .clk_i, .rst_ni, - .in, - .d_o, + .data_i, + .data_o, .syndrome_o, .err_o, .error_inject_i diff --git a/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_secded_22_16_fpv.sv b/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_secded_22_16_fpv.sv index 9d1e56ff..46dfb164 100644 --- a/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_secded_22_16_fpv.sv +++ b/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_secded_22_16_fpv.sv @@ -7,8 +7,8 @@ module prim_secded_22_16_fpv ( input clk_i, input rst_ni, - input [15:0] in, - output logic [15:0] d_o, + input [15:0] data_i, + output logic [15:0] data_o, output logic [5:0] syndrome_o, output logic [1:0] err_o, input [21:0] error_inject_i @@ -17,13 +17,13 @@ module prim_secded_22_16_fpv ( logic [21:0] data_enc; prim_secded_22_16_enc prim_secded_22_16_enc ( - .in, - .out(data_enc) + .data_i, + .data_o(data_enc) ); prim_secded_22_16_dec prim_secded_22_16_dec ( - .in(data_enc ^ error_inject_i), - .d_o, + .data_i(data_enc ^ error_inject_i), + .data_o, .syndrome_o, .err_o ); diff --git a/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_secded_28_22_bind_fpv.sv b/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_secded_28_22_bind_fpv.sv index aed16ca3..24208d3f 100644 --- a/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_secded_28_22_bind_fpv.sv +++ b/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_secded_28_22_bind_fpv.sv @@ -10,8 +10,8 @@ module prim_secded_28_22_bind_fpv; prim_secded_28_22_assert_fpv prim_secded_28_22_assert_fpv ( .clk_i, .rst_ni, - .in, - .d_o, + .data_i, + .data_o, .syndrome_o, .err_o, .error_inject_i diff --git a/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_secded_28_22_fpv.sv b/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_secded_28_22_fpv.sv index 2f2f8eda..d2db2564 100644 --- a/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_secded_28_22_fpv.sv +++ b/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_secded_28_22_fpv.sv @@ -7,8 +7,8 @@ module prim_secded_28_22_fpv ( input clk_i, input rst_ni, - input [21:0] in, - output logic [21:0] d_o, + input [21:0] data_i, + output logic [21:0] data_o, output logic [5:0] syndrome_o, output logic [1:0] err_o, input [27:0] error_inject_i @@ -17,13 +17,13 @@ module prim_secded_28_22_fpv ( logic [27:0] data_enc; prim_secded_28_22_enc prim_secded_28_22_enc ( - .in, - .out(data_enc) + .data_i, + .data_o(data_enc) ); prim_secded_28_22_dec prim_secded_28_22_dec ( - .in(data_enc ^ error_inject_i), - .d_o, + .data_i(data_enc ^ error_inject_i), + .data_o, .syndrome_o, .err_o ); diff --git a/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_secded_39_32_bind_fpv.sv b/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_secded_39_32_bind_fpv.sv index 29bf2506..113dd56d 100644 --- a/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_secded_39_32_bind_fpv.sv +++ b/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_secded_39_32_bind_fpv.sv @@ -10,8 +10,8 @@ module prim_secded_39_32_bind_fpv; prim_secded_39_32_assert_fpv prim_secded_39_32_assert_fpv ( .clk_i, .rst_ni, - .in, - .d_o, + .data_i, + .data_o, .syndrome_o, .err_o, .error_inject_i diff --git a/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_secded_39_32_fpv.sv b/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_secded_39_32_fpv.sv index f848f457..34fbf2a6 100644 --- a/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_secded_39_32_fpv.sv +++ b/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_secded_39_32_fpv.sv @@ -7,8 +7,8 @@ module prim_secded_39_32_fpv ( input clk_i, input rst_ni, - input [31:0] in, - output logic [31:0] d_o, + input [31:0] data_i, + output logic [31:0] data_o, output logic [6:0] syndrome_o, output logic [1:0] err_o, input [38:0] error_inject_i @@ -17,13 +17,13 @@ module prim_secded_39_32_fpv ( logic [38:0] data_enc; prim_secded_39_32_enc prim_secded_39_32_enc ( - .in, - .out(data_enc) + .data_i, + .data_o(data_enc) ); prim_secded_39_32_dec prim_secded_39_32_dec ( - .in(data_enc ^ error_inject_i), - .d_o, + .data_i(data_enc ^ error_inject_i), + .data_o, .syndrome_o, .err_o ); diff --git a/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_secded_64_57_bind_fpv.sv b/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_secded_64_57_bind_fpv.sv index 1ef65e29..7d9542c0 100644 --- a/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_secded_64_57_bind_fpv.sv +++ b/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_secded_64_57_bind_fpv.sv @@ -10,8 +10,8 @@ module prim_secded_64_57_bind_fpv; prim_secded_64_57_assert_fpv prim_secded_64_57_assert_fpv ( .clk_i, .rst_ni, - .in, - .d_o, + .data_i, + .data_o, .syndrome_o, .err_o, .error_inject_i diff --git a/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_secded_64_57_fpv.sv b/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_secded_64_57_fpv.sv index 6827db72..7b735a1a 100644 --- a/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_secded_64_57_fpv.sv +++ b/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_secded_64_57_fpv.sv @@ -7,8 +7,8 @@ module prim_secded_64_57_fpv ( input clk_i, input rst_ni, - input [56:0] in, - output logic [56:0] d_o, + input [56:0] data_i, + output logic [56:0] data_o, output logic [6:0] syndrome_o, output logic [1:0] err_o, input [63:0] error_inject_i @@ -17,13 +17,13 @@ module prim_secded_64_57_fpv ( logic [63:0] data_enc; prim_secded_64_57_enc prim_secded_64_57_enc ( - .in, - .out(data_enc) + .data_i, + .data_o(data_enc) ); prim_secded_64_57_dec prim_secded_64_57_dec ( - .in(data_enc ^ error_inject_i), - .d_o, + .data_i(data_enc ^ error_inject_i), + .data_o, .syndrome_o, .err_o ); diff --git a/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_secded_72_64_bind_fpv.sv b/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_secded_72_64_bind_fpv.sv index a3285d2f..9684f65e 100644 --- a/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_secded_72_64_bind_fpv.sv +++ b/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_secded_72_64_bind_fpv.sv @@ -10,8 +10,8 @@ module prim_secded_72_64_bind_fpv; prim_secded_72_64_assert_fpv prim_secded_72_64_assert_fpv ( .clk_i, .rst_ni, - .in, - .d_o, + .data_i, + .data_o, .syndrome_o, .err_o, .error_inject_i diff --git a/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_secded_72_64_fpv.sv b/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_secded_72_64_fpv.sv index cbe2c8bb..0e697800 100644 --- a/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_secded_72_64_fpv.sv +++ b/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_secded_72_64_fpv.sv @@ -7,8 +7,8 @@ module prim_secded_72_64_fpv ( input clk_i, input rst_ni, - input [63:0] in, - output logic [63:0] d_o, + input [63:0] data_i, + output logic [63:0] data_o, output logic [7:0] syndrome_o, output logic [1:0] err_o, input [71:0] error_inject_i @@ -17,13 +17,13 @@ module prim_secded_72_64_fpv ( logic [71:0] data_enc; prim_secded_72_64_enc prim_secded_72_64_enc ( - .in, - .out(data_enc) + .data_i, + .data_o(data_enc) ); prim_secded_72_64_dec prim_secded_72_64_dec ( - .in(data_enc ^ error_inject_i), - .d_o, + .data_i(data_enc ^ error_inject_i), + .data_o, .syndrome_o, .err_o ); diff --git a/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_secded_hamming_22_16_bind_fpv.sv b/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_secded_hamming_22_16_bind_fpv.sv index 42a8e63d..e887a7cb 100644 --- a/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_secded_hamming_22_16_bind_fpv.sv +++ b/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_secded_hamming_22_16_bind_fpv.sv @@ -10,8 +10,8 @@ module prim_secded_hamming_22_16_bind_fpv; prim_secded_hamming_22_16_assert_fpv prim_secded_hamming_22_16_assert_fpv ( .clk_i, .rst_ni, - .in, - .d_o, + .data_i, + .data_o, .syndrome_o, .err_o, .error_inject_i diff --git a/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_secded_hamming_22_16_fpv.sv b/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_secded_hamming_22_16_fpv.sv index 25718057..9787c196 100644 --- a/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_secded_hamming_22_16_fpv.sv +++ b/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_secded_hamming_22_16_fpv.sv @@ -7,8 +7,8 @@ module prim_secded_hamming_22_16_fpv ( input clk_i, input rst_ni, - input [15:0] in, - output logic [15:0] d_o, + input [15:0] data_i, + output logic [15:0] data_o, output logic [5:0] syndrome_o, output logic [1:0] err_o, input [21:0] error_inject_i @@ -17,13 +17,13 @@ module prim_secded_hamming_22_16_fpv ( logic [21:0] data_enc; prim_secded_hamming_22_16_enc prim_secded_hamming_22_16_enc ( - .in, - .out(data_enc) + .data_i, + .data_o(data_enc) ); prim_secded_hamming_22_16_dec prim_secded_hamming_22_16_dec ( - .in(data_enc ^ error_inject_i), - .d_o, + .data_i(data_enc ^ error_inject_i), + .data_o, .syndrome_o, .err_o ); diff --git a/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_secded_hamming_39_32_bind_fpv.sv b/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_secded_hamming_39_32_bind_fpv.sv index d32d1e96..f4895e58 100644 --- a/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_secded_hamming_39_32_bind_fpv.sv +++ b/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_secded_hamming_39_32_bind_fpv.sv @@ -10,8 +10,8 @@ module prim_secded_hamming_39_32_bind_fpv; prim_secded_hamming_39_32_assert_fpv prim_secded_hamming_39_32_assert_fpv ( .clk_i, .rst_ni, - .in, - .d_o, + .data_i, + .data_o, .syndrome_o, .err_o, .error_inject_i diff --git a/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_secded_hamming_39_32_fpv.sv b/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_secded_hamming_39_32_fpv.sv index 32c49f4b..1b8f8b73 100644 --- a/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_secded_hamming_39_32_fpv.sv +++ b/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_secded_hamming_39_32_fpv.sv @@ -7,8 +7,8 @@ module prim_secded_hamming_39_32_fpv ( input clk_i, input rst_ni, - input [31:0] in, - output logic [31:0] d_o, + input [31:0] data_i, + output logic [31:0] data_o, output logic [6:0] syndrome_o, output logic [1:0] err_o, input [38:0] error_inject_i @@ -17,13 +17,13 @@ module prim_secded_hamming_39_32_fpv ( logic [38:0] data_enc; prim_secded_hamming_39_32_enc prim_secded_hamming_39_32_enc ( - .in, - .out(data_enc) + .data_i, + .data_o(data_enc) ); prim_secded_hamming_39_32_dec prim_secded_hamming_39_32_dec ( - .in(data_enc ^ error_inject_i), - .d_o, + .data_i(data_enc ^ error_inject_i), + .data_o, .syndrome_o, .err_o ); diff --git a/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_secded_hamming_72_64_bind_fpv.sv b/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_secded_hamming_72_64_bind_fpv.sv index 147fad1e..c167775e 100644 --- a/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_secded_hamming_72_64_bind_fpv.sv +++ b/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_secded_hamming_72_64_bind_fpv.sv @@ -10,8 +10,8 @@ module prim_secded_hamming_72_64_bind_fpv; prim_secded_hamming_72_64_assert_fpv prim_secded_hamming_72_64_assert_fpv ( .clk_i, .rst_ni, - .in, - .d_o, + .data_i, + .data_o, .syndrome_o, .err_o, .error_inject_i diff --git a/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_secded_hamming_72_64_fpv.sv b/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_secded_hamming_72_64_fpv.sv index 6b3322fe..c8deaef0 100644 --- a/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_secded_hamming_72_64_fpv.sv +++ b/vendor/lowrisc_ip/ip/prim/fpv/tb/prim_secded_hamming_72_64_fpv.sv @@ -7,8 +7,8 @@ module prim_secded_hamming_72_64_fpv ( input clk_i, input rst_ni, - input [63:0] in, - output logic [63:0] d_o, + input [63:0] data_i, + output logic [63:0] data_o, output logic [7:0] syndrome_o, output logic [1:0] err_o, input [71:0] error_inject_i @@ -17,13 +17,13 @@ module prim_secded_hamming_72_64_fpv ( logic [71:0] data_enc; prim_secded_hamming_72_64_enc prim_secded_hamming_72_64_enc ( - .in, - .out(data_enc) + .data_i, + .data_o(data_enc) ); prim_secded_hamming_72_64_dec prim_secded_hamming_72_64_dec ( - .in(data_enc ^ error_inject_i), - .d_o, + .data_i(data_enc ^ error_inject_i), + .data_o, .syndrome_o, .err_o ); diff --git a/vendor/lowrisc_ip/ip/prim/fpv/vip/prim_esc_rxtx_assert_fpv.sv b/vendor/lowrisc_ip/ip/prim/fpv/vip/prim_esc_rxtx_assert_fpv.sv index 3c0b659d..40a1547f 100644 --- a/vendor/lowrisc_ip/ip/prim/fpv/vip/prim_esc_rxtx_assert_fpv.sv +++ b/vendor/lowrisc_ip/ip/prim/fpv/vip/prim_esc_rxtx_assert_fpv.sv @@ -24,47 +24,147 @@ module prim_esc_rxtx_assert_fpv ( ); logic error_present; - assign error_present = resp_err_pi | resp_err_ni | - esc_err_pi | esc_err_ni; + assign error_present = resp_err_pi || + resp_err_ni || + esc_err_pi || + esc_err_ni; + + // tracks whether any error has been injected so far + logic error_d, error_q; + assign error_d = error_q || error_present; + always_ff @(posedge clk_i or negedge rst_ni) begin : p_error_reg + if (!rst_ni) begin + error_q <= 1'b0; + end else begin + error_q <= error_d; + end + end + + // tracks whether escalation has been triggered so far + logic esc_d, esc_q; + assign esc_d = esc_q || esc_req_i; + always_ff @(posedge clk_i or negedge rst_ni) begin : p_esc_reg + if (!rst_ni) begin + esc_q <= 1'b0; + end else begin + esc_q <= esc_d; + end + end // ping will stay high until ping ok received, then it must be deasserted - // TODO: this escludes the case where no ping ok will be returned due to an error - `ASSUME_FPV(PingDeassert_M, ping_req_i && ping_ok_o |=> ping_req_i, clk_i, !rst_ni) - `ASSUME_FPV(PingEnStaysAsserted0_M, ping_req_i |=> - (ping_req_i && !ping_ok_o) or (ping_req_i && ping_ok_o ##1 $fell(ping_req_i)), - clk_i, !rst_ni || error_present) + `ASSUME_FPV(PingReqDeassert_M, + ping_req_i && + ping_ok_o + |=> + !ping_req_i) + `ASSUME_FPV(PingReqStaysAsserted0_M, + ping_req_i && + !ping_ok_o + |=> + ping_req_i) + // this timing is guaranteed by the lfsr ping timer. + `ASSUME_FPV(PingReqStaysLowFor3Cycles_M, + $fell(ping_req_i) + |-> + !ping_req_i [*3]) - // assume that the ping enable and escalation enable signals will eventually be deasserted (and - // esc will stay low for more than 2 cycles) - `ASSUME_FPV(FiniteEsc_M, esc_req_i |-> strong(##[1:$] !esc_req_i [*2])) - `ASSUME_FPV(FinitePing_M, ping_req_i |-> strong(##[1:$] !ping_req_i)) - // ping response mus occur within 4 cycles (given that no - // error occured within the previous cycles) - `ASSERT(PingRespCheck_A, !error_present [*4] ##1 $rose(ping_req_i) |-> - ##[0:4] ping_ok_o, clk_i, !rst_ni || error_present) + // assume that escalation enable signal will eventually be deasserted + // for more than 3 cycles (this assumption is needed such that the FSM liveness + // assertion below can be proven). + `ASSUME_FPV(FiniteEsc_M, + esc_req_i + |-> + strong(##[1:$] !esc_req_i [*3])) - // be more specific (i.e. use throughout) - `ASSERT(EscRespCheck_A, ##1 esc_req_i |-> ##[0:1] prim_esc_rxtx_fpv.esc_rx_out.resp_p ##1 - !prim_esc_rxtx_fpv.esc_rx_out.resp_p, clk_i, !rst_ni || error_present) + // check that ping response time is bounded if no error has occurred so far, and + // no escalation is being requested. + `ASSERT(PingRespCheck_A, + $rose(ping_req_i) && + !esc_req_i + |-> + ##[0:4] ping_ok_o, + clk_i, + !rst_ni || + error_d || + esc_req_i) + + // check escalation response toggles. + `ASSERT(EscRespCheck_A, + ##1 esc_req_i + |-> + ##[0:1] prim_esc_rxtx_fpv.esc_rx_out.resp_p + ##1 !prim_esc_rxtx_fpv.esc_rx_out.resp_p, + clk_i, + !rst_ni || + error_present) // check correct transmission of escalation within 0-1 cycles - `ASSERT(EscCheck_A, ##1 esc_req_i |-> ##[0:1] esc_en_o, clk_i, !rst_ni || error_present) + `ASSERT(EscCheck_A, + ##1 esc_req_i + |-> + ##[0:1] esc_en_o, + clk_i, + !rst_ni || + error_present) // check that a single error on the diffpairs is detected - `ASSERT(SingleSigIntDetected0_A, {esc_err_pi, esc_err_ni} == '0 ##1 - $onehot({resp_err_pi, resp_err_ni}) |-> integ_fail_o) - `ASSERT(SingleSigIntDetected1_A, $onehot({esc_err_pi, esc_err_ni}) ##1 - {resp_err_pi, resp_err_ni} == '0 |-> integ_fail_o) + `ASSERT(SingleSigIntDetected0_A, + {esc_err_pi, esc_err_ni} == '0 ##1 + $onehot({resp_err_pi, resp_err_ni}) + |-> + integ_fail_o) + `ASSERT(SingleSigIntDetected1_A, + $onehot({esc_err_pi, esc_err_ni}) ##1 + {resp_err_pi, resp_err_ni} == '0 + |-> + integ_fail_o) + + // basic liveness of sender FSM + `ASSERT(FsmLivenessSender_A, + (prim_esc_rxtx_fpv.u_prim_esc_sender.state_q != + prim_esc_rxtx_fpv.u_prim_esc_sender.Idle) + |-> + strong(##[1:$] (prim_esc_rxtx_fpv.u_prim_esc_sender.state_q + == prim_esc_rxtx_fpv.u_prim_esc_sender.Idle))) + // basic liveness of sender FSM (can only be guaranteed if no error is present) + `ASSERT(FsmLivenessReceiver_A, + (prim_esc_rxtx_fpv.u_prim_esc_receiver.state_q != + prim_esc_rxtx_fpv.u_prim_esc_receiver.Idle) + |-> + strong(##[1:$] (prim_esc_rxtx_fpv.u_prim_esc_receiver.state_q + == prim_esc_rxtx_fpv.u_prim_esc_receiver.Idle)), + clk_i, + rst_ni || + error_present) + + // check that auto escalation timeout does not trigger prematurely. + // this requires that no errors have been present so far. + `ASSERT(AutoEscalation0_A, + ping_req_i && + ping_ok_o && + !esc_en_o ##1 + !ping_req_i [*0 : 2**prim_esc_rxtx_fpv.u_prim_esc_receiver.TimeoutCntDw - 4] + |-> + !esc_en_o, + clk_i, + !rst_ni || + error_d || + esc_d) + + // check that auto escalation timeout kicks in if pings are absent for too long. + // this requires that no errors have been present so far. + `ASSERT(AutoEscalation1_A, + ping_req_i && + ping_ok_o && + !esc_en_o ##1 + !ping_req_i [* 2**prim_esc_rxtx_fpv.u_prim_esc_receiver.TimeoutCntDw - 3 : $] + |-> + esc_en_o, + clk_i, + !rst_ni || + error_d || + esc_d) - // basic liveness of FSMs in case no errors are present - `ASSERT(FsmLivenessSender_A, (prim_esc_rxtx_fpv.i_prim_esc_sender.state_q != - prim_esc_rxtx_fpv.i_prim_esc_sender.Idle) |-> - strong(##[1:$] (prim_esc_rxtx_fpv.i_prim_esc_sender.state_q - == prim_esc_rxtx_fpv.i_prim_esc_sender.Idle)), clk_i, !rst_ni || error_present) - `ASSERT(FsmLivenessReceiver_A, (prim_esc_rxtx_fpv.i_prim_esc_receiver.state_q != - prim_esc_rxtx_fpv.i_prim_esc_receiver.Idle) |-> - strong(##[1:$] (prim_esc_rxtx_fpv.i_prim_esc_receiver.state_q - == prim_esc_rxtx_fpv.i_prim_esc_receiver.Idle)), clk_i, !rst_ni || error_present) endmodule : prim_esc_rxtx_assert_fpv diff --git a/vendor/lowrisc_ip/ip/prim/fpv/vip/prim_secded_22_16_assert_fpv.sv b/vendor/lowrisc_ip/ip/prim/fpv/vip/prim_secded_22_16_assert_fpv.sv index 3178743b..ff752795 100644 --- a/vendor/lowrisc_ip/ip/prim/fpv/vip/prim_secded_22_16_assert_fpv.sv +++ b/vendor/lowrisc_ip/ip/prim/fpv/vip/prim_secded_22_16_assert_fpv.sv @@ -7,8 +7,8 @@ module prim_secded_22_16_assert_fpv ( input clk_i, input rst_ni, - input [15:0] in, - input [15:0] d_o, + input [15:0] data_i, + input [15:0] data_o, input [5:0] syndrome_o, input [1:0] err_o, input [21:0] error_inject_i @@ -17,7 +17,7 @@ module prim_secded_22_16_assert_fpv ( // Inject a maximum of two errors simultaneously. `ASSUME_FPV(MaxTwoErrors_M, $countones(error_inject_i) <= 2) // This bounds the input data state space to make sure the solver converges. - `ASSUME_FPV(DataLimit_M, $onehot0(in) || $onehot0(~in)) + `ASSUME_FPV(DataLimit_M, $onehot0(data_i) || $onehot0(~data_i)) // Single bit error detection `ASSERT(SingleErrorDetect_A, $countones(error_inject_i) == 1 |-> err_o[0]) `ASSERT(SingleErrorDetectReverse_A, err_o[0] |-> $countones(error_inject_i) == 1) @@ -25,7 +25,7 @@ module prim_secded_22_16_assert_fpv ( `ASSERT(DoubleErrorDetect_A, $countones(error_inject_i) == 2 |-> err_o[1]) `ASSERT(DoubleErrorDetectReverse_A, err_o[1] |-> $countones(error_inject_i) == 2) // Single bit error correction (implicitly tests the syndrome output) - `ASSERT(SingleErrorCorrect_A, $countones(error_inject_i) < 2 |-> in == d_o) + `ASSERT(SingleErrorCorrect_A, $countones(error_inject_i) < 2 |-> data_i == data_o) // Basic syndrome check `ASSERT(SyndromeCheck_A, |syndrome_o |-> $countones(error_inject_i) > 0) `ASSERT(SyndromeCheckReverse_A, $countones(error_inject_i) > 0 |-> |syndrome_o) diff --git a/vendor/lowrisc_ip/ip/prim/fpv/vip/prim_secded_28_22_assert_fpv.sv b/vendor/lowrisc_ip/ip/prim/fpv/vip/prim_secded_28_22_assert_fpv.sv index 0ea98056..7b39b91e 100644 --- a/vendor/lowrisc_ip/ip/prim/fpv/vip/prim_secded_28_22_assert_fpv.sv +++ b/vendor/lowrisc_ip/ip/prim/fpv/vip/prim_secded_28_22_assert_fpv.sv @@ -7,8 +7,8 @@ module prim_secded_28_22_assert_fpv ( input clk_i, input rst_ni, - input [21:0] in, - input [21:0] d_o, + input [21:0] data_i, + input [21:0] data_o, input [5:0] syndrome_o, input [1:0] err_o, input [27:0] error_inject_i @@ -17,7 +17,7 @@ module prim_secded_28_22_assert_fpv ( // Inject a maximum of two errors simultaneously. `ASSUME_FPV(MaxTwoErrors_M, $countones(error_inject_i) <= 2) // This bounds the input data state space to make sure the solver converges. - `ASSUME_FPV(DataLimit_M, $onehot0(in) || $onehot0(~in)) + `ASSUME_FPV(DataLimit_M, $onehot0(data_i) || $onehot0(~data_i)) // Single bit error detection `ASSERT(SingleErrorDetect_A, $countones(error_inject_i) == 1 |-> err_o[0]) `ASSERT(SingleErrorDetectReverse_A, err_o[0] |-> $countones(error_inject_i) == 1) @@ -25,7 +25,7 @@ module prim_secded_28_22_assert_fpv ( `ASSERT(DoubleErrorDetect_A, $countones(error_inject_i) == 2 |-> err_o[1]) `ASSERT(DoubleErrorDetectReverse_A, err_o[1] |-> $countones(error_inject_i) == 2) // Single bit error correction (implicitly tests the syndrome output) - `ASSERT(SingleErrorCorrect_A, $countones(error_inject_i) < 2 |-> in == d_o) + `ASSERT(SingleErrorCorrect_A, $countones(error_inject_i) < 2 |-> data_i == data_o) // Basic syndrome check `ASSERT(SyndromeCheck_A, |syndrome_o |-> $countones(error_inject_i) > 0) `ASSERT(SyndromeCheckReverse_A, $countones(error_inject_i) > 0 |-> |syndrome_o) diff --git a/vendor/lowrisc_ip/ip/prim/fpv/vip/prim_secded_39_32_assert_fpv.sv b/vendor/lowrisc_ip/ip/prim/fpv/vip/prim_secded_39_32_assert_fpv.sv index 0168213e..9d5f57e6 100644 --- a/vendor/lowrisc_ip/ip/prim/fpv/vip/prim_secded_39_32_assert_fpv.sv +++ b/vendor/lowrisc_ip/ip/prim/fpv/vip/prim_secded_39_32_assert_fpv.sv @@ -7,8 +7,8 @@ module prim_secded_39_32_assert_fpv ( input clk_i, input rst_ni, - input [31:0] in, - input [31:0] d_o, + input [31:0] data_i, + input [31:0] data_o, input [6:0] syndrome_o, input [1:0] err_o, input [38:0] error_inject_i @@ -17,7 +17,7 @@ module prim_secded_39_32_assert_fpv ( // Inject a maximum of two errors simultaneously. `ASSUME_FPV(MaxTwoErrors_M, $countones(error_inject_i) <= 2) // This bounds the input data state space to make sure the solver converges. - `ASSUME_FPV(DataLimit_M, $onehot0(in) || $onehot0(~in)) + `ASSUME_FPV(DataLimit_M, $onehot0(data_i) || $onehot0(~data_i)) // Single bit error detection `ASSERT(SingleErrorDetect_A, $countones(error_inject_i) == 1 |-> err_o[0]) `ASSERT(SingleErrorDetectReverse_A, err_o[0] |-> $countones(error_inject_i) == 1) @@ -25,7 +25,7 @@ module prim_secded_39_32_assert_fpv ( `ASSERT(DoubleErrorDetect_A, $countones(error_inject_i) == 2 |-> err_o[1]) `ASSERT(DoubleErrorDetectReverse_A, err_o[1] |-> $countones(error_inject_i) == 2) // Single bit error correction (implicitly tests the syndrome output) - `ASSERT(SingleErrorCorrect_A, $countones(error_inject_i) < 2 |-> in == d_o) + `ASSERT(SingleErrorCorrect_A, $countones(error_inject_i) < 2 |-> data_i == data_o) // Basic syndrome check `ASSERT(SyndromeCheck_A, |syndrome_o |-> $countones(error_inject_i) > 0) `ASSERT(SyndromeCheckReverse_A, $countones(error_inject_i) > 0 |-> |syndrome_o) diff --git a/vendor/lowrisc_ip/ip/prim/fpv/vip/prim_secded_64_57_assert_fpv.sv b/vendor/lowrisc_ip/ip/prim/fpv/vip/prim_secded_64_57_assert_fpv.sv index ca9c8300..eb324337 100644 --- a/vendor/lowrisc_ip/ip/prim/fpv/vip/prim_secded_64_57_assert_fpv.sv +++ b/vendor/lowrisc_ip/ip/prim/fpv/vip/prim_secded_64_57_assert_fpv.sv @@ -7,8 +7,8 @@ module prim_secded_64_57_assert_fpv ( input clk_i, input rst_ni, - input [56:0] in, - input [56:0] d_o, + input [56:0] data_i, + input [56:0] data_o, input [6:0] syndrome_o, input [1:0] err_o, input [63:0] error_inject_i @@ -17,7 +17,7 @@ module prim_secded_64_57_assert_fpv ( // Inject a maximum of two errors simultaneously. `ASSUME_FPV(MaxTwoErrors_M, $countones(error_inject_i) <= 2) // This bounds the input data state space to make sure the solver converges. - `ASSUME_FPV(DataLimit_M, $onehot0(in) || $onehot0(~in)) + `ASSUME_FPV(DataLimit_M, $onehot0(data_i) || $onehot0(~data_i)) // Single bit error detection `ASSERT(SingleErrorDetect_A, $countones(error_inject_i) == 1 |-> err_o[0]) `ASSERT(SingleErrorDetectReverse_A, err_o[0] |-> $countones(error_inject_i) == 1) @@ -25,7 +25,7 @@ module prim_secded_64_57_assert_fpv ( `ASSERT(DoubleErrorDetect_A, $countones(error_inject_i) == 2 |-> err_o[1]) `ASSERT(DoubleErrorDetectReverse_A, err_o[1] |-> $countones(error_inject_i) == 2) // Single bit error correction (implicitly tests the syndrome output) - `ASSERT(SingleErrorCorrect_A, $countones(error_inject_i) < 2 |-> in == d_o) + `ASSERT(SingleErrorCorrect_A, $countones(error_inject_i) < 2 |-> data_i == data_o) // Basic syndrome check `ASSERT(SyndromeCheck_A, |syndrome_o |-> $countones(error_inject_i) > 0) `ASSERT(SyndromeCheckReverse_A, $countones(error_inject_i) > 0 |-> |syndrome_o) diff --git a/vendor/lowrisc_ip/ip/prim/fpv/vip/prim_secded_72_64_assert_fpv.sv b/vendor/lowrisc_ip/ip/prim/fpv/vip/prim_secded_72_64_assert_fpv.sv index 390345da..7fd00664 100644 --- a/vendor/lowrisc_ip/ip/prim/fpv/vip/prim_secded_72_64_assert_fpv.sv +++ b/vendor/lowrisc_ip/ip/prim/fpv/vip/prim_secded_72_64_assert_fpv.sv @@ -7,8 +7,8 @@ module prim_secded_72_64_assert_fpv ( input clk_i, input rst_ni, - input [63:0] in, - input [63:0] d_o, + input [63:0] data_i, + input [63:0] data_o, input [7:0] syndrome_o, input [1:0] err_o, input [71:0] error_inject_i @@ -17,7 +17,7 @@ module prim_secded_72_64_assert_fpv ( // Inject a maximum of two errors simultaneously. `ASSUME_FPV(MaxTwoErrors_M, $countones(error_inject_i) <= 2) // This bounds the input data state space to make sure the solver converges. - `ASSUME_FPV(DataLimit_M, $onehot0(in) || $onehot0(~in)) + `ASSUME_FPV(DataLimit_M, $onehot0(data_i) || $onehot0(~data_i)) // Single bit error detection `ASSERT(SingleErrorDetect_A, $countones(error_inject_i) == 1 |-> err_o[0]) `ASSERT(SingleErrorDetectReverse_A, err_o[0] |-> $countones(error_inject_i) == 1) @@ -25,7 +25,7 @@ module prim_secded_72_64_assert_fpv ( `ASSERT(DoubleErrorDetect_A, $countones(error_inject_i) == 2 |-> err_o[1]) `ASSERT(DoubleErrorDetectReverse_A, err_o[1] |-> $countones(error_inject_i) == 2) // Single bit error correction (implicitly tests the syndrome output) - `ASSERT(SingleErrorCorrect_A, $countones(error_inject_i) < 2 |-> in == d_o) + `ASSERT(SingleErrorCorrect_A, $countones(error_inject_i) < 2 |-> data_i == data_o) // Basic syndrome check `ASSERT(SyndromeCheck_A, |syndrome_o |-> $countones(error_inject_i) > 0) `ASSERT(SyndromeCheckReverse_A, $countones(error_inject_i) > 0 |-> |syndrome_o) diff --git a/vendor/lowrisc_ip/ip/prim/fpv/vip/prim_secded_hamming_22_16_assert_fpv.sv b/vendor/lowrisc_ip/ip/prim/fpv/vip/prim_secded_hamming_22_16_assert_fpv.sv index 35e6a542..600576b2 100644 --- a/vendor/lowrisc_ip/ip/prim/fpv/vip/prim_secded_hamming_22_16_assert_fpv.sv +++ b/vendor/lowrisc_ip/ip/prim/fpv/vip/prim_secded_hamming_22_16_assert_fpv.sv @@ -7,8 +7,8 @@ module prim_secded_hamming_22_16_assert_fpv ( input clk_i, input rst_ni, - input [15:0] in, - input [15:0] d_o, + input [15:0] data_i, + input [15:0] data_o, input [5:0] syndrome_o, input [1:0] err_o, input [21:0] error_inject_i @@ -17,7 +17,7 @@ module prim_secded_hamming_22_16_assert_fpv ( // Inject a maximum of two errors simultaneously. `ASSUME_FPV(MaxTwoErrors_M, $countones(error_inject_i) <= 2) // This bounds the input data state space to make sure the solver converges. - `ASSUME_FPV(DataLimit_M, $onehot0(in) || $onehot0(~in)) + `ASSUME_FPV(DataLimit_M, $onehot0(data_i) || $onehot0(~data_i)) // Single bit error detection `ASSERT(SingleErrorDetect_A, $countones(error_inject_i) == 1 |-> err_o[0]) `ASSERT(SingleErrorDetectReverse_A, err_o[0] |-> $countones(error_inject_i) == 1) @@ -25,7 +25,7 @@ module prim_secded_hamming_22_16_assert_fpv ( `ASSERT(DoubleErrorDetect_A, $countones(error_inject_i) == 2 |-> err_o[1]) `ASSERT(DoubleErrorDetectReverse_A, err_o[1] |-> $countones(error_inject_i) == 2) // Single bit error correction (implicitly tests the syndrome output) - `ASSERT(SingleErrorCorrect_A, $countones(error_inject_i) < 2 |-> in == d_o) + `ASSERT(SingleErrorCorrect_A, $countones(error_inject_i) < 2 |-> data_i == data_o) // Basic syndrome check `ASSERT(SyndromeCheck_A, |syndrome_o |-> $countones(error_inject_i) > 0) `ASSERT(SyndromeCheckReverse_A, $countones(error_inject_i) > 0 |-> |syndrome_o) diff --git a/vendor/lowrisc_ip/ip/prim/fpv/vip/prim_secded_hamming_39_32_assert_fpv.sv b/vendor/lowrisc_ip/ip/prim/fpv/vip/prim_secded_hamming_39_32_assert_fpv.sv index 8e45b1e5..1b2c0b4a 100644 --- a/vendor/lowrisc_ip/ip/prim/fpv/vip/prim_secded_hamming_39_32_assert_fpv.sv +++ b/vendor/lowrisc_ip/ip/prim/fpv/vip/prim_secded_hamming_39_32_assert_fpv.sv @@ -7,8 +7,8 @@ module prim_secded_hamming_39_32_assert_fpv ( input clk_i, input rst_ni, - input [31:0] in, - input [31:0] d_o, + input [31:0] data_i, + input [31:0] data_o, input [6:0] syndrome_o, input [1:0] err_o, input [38:0] error_inject_i @@ -17,7 +17,7 @@ module prim_secded_hamming_39_32_assert_fpv ( // Inject a maximum of two errors simultaneously. `ASSUME_FPV(MaxTwoErrors_M, $countones(error_inject_i) <= 2) // This bounds the input data state space to make sure the solver converges. - `ASSUME_FPV(DataLimit_M, $onehot0(in) || $onehot0(~in)) + `ASSUME_FPV(DataLimit_M, $onehot0(data_i) || $onehot0(~data_i)) // Single bit error detection `ASSERT(SingleErrorDetect_A, $countones(error_inject_i) == 1 |-> err_o[0]) `ASSERT(SingleErrorDetectReverse_A, err_o[0] |-> $countones(error_inject_i) == 1) @@ -25,7 +25,7 @@ module prim_secded_hamming_39_32_assert_fpv ( `ASSERT(DoubleErrorDetect_A, $countones(error_inject_i) == 2 |-> err_o[1]) `ASSERT(DoubleErrorDetectReverse_A, err_o[1] |-> $countones(error_inject_i) == 2) // Single bit error correction (implicitly tests the syndrome output) - `ASSERT(SingleErrorCorrect_A, $countones(error_inject_i) < 2 |-> in == d_o) + `ASSERT(SingleErrorCorrect_A, $countones(error_inject_i) < 2 |-> data_i == data_o) // Basic syndrome check `ASSERT(SyndromeCheck_A, |syndrome_o |-> $countones(error_inject_i) > 0) `ASSERT(SyndromeCheckReverse_A, $countones(error_inject_i) > 0 |-> |syndrome_o) diff --git a/vendor/lowrisc_ip/ip/prim/fpv/vip/prim_secded_hamming_72_64_assert_fpv.sv b/vendor/lowrisc_ip/ip/prim/fpv/vip/prim_secded_hamming_72_64_assert_fpv.sv index 307941e9..fed2707d 100644 --- a/vendor/lowrisc_ip/ip/prim/fpv/vip/prim_secded_hamming_72_64_assert_fpv.sv +++ b/vendor/lowrisc_ip/ip/prim/fpv/vip/prim_secded_hamming_72_64_assert_fpv.sv @@ -7,8 +7,8 @@ module prim_secded_hamming_72_64_assert_fpv ( input clk_i, input rst_ni, - input [63:0] in, - input [63:0] d_o, + input [63:0] data_i, + input [63:0] data_o, input [7:0] syndrome_o, input [1:0] err_o, input [71:0] error_inject_i @@ -17,7 +17,7 @@ module prim_secded_hamming_72_64_assert_fpv ( // Inject a maximum of two errors simultaneously. `ASSUME_FPV(MaxTwoErrors_M, $countones(error_inject_i) <= 2) // This bounds the input data state space to make sure the solver converges. - `ASSUME_FPV(DataLimit_M, $onehot0(in) || $onehot0(~in)) + `ASSUME_FPV(DataLimit_M, $onehot0(data_i) || $onehot0(~data_i)) // Single bit error detection `ASSERT(SingleErrorDetect_A, $countones(error_inject_i) == 1 |-> err_o[0]) `ASSERT(SingleErrorDetectReverse_A, err_o[0] |-> $countones(error_inject_i) == 1) @@ -25,7 +25,7 @@ module prim_secded_hamming_72_64_assert_fpv ( `ASSERT(DoubleErrorDetect_A, $countones(error_inject_i) == 2 |-> err_o[1]) `ASSERT(DoubleErrorDetectReverse_A, err_o[1] |-> $countones(error_inject_i) == 2) // Single bit error correction (implicitly tests the syndrome output) - `ASSERT(SingleErrorCorrect_A, $countones(error_inject_i) < 2 |-> in == d_o) + `ASSERT(SingleErrorCorrect_A, $countones(error_inject_i) < 2 |-> data_i == data_o) // Basic syndrome check `ASSERT(SyndromeCheck_A, |syndrome_o |-> $countones(error_inject_i) > 0) `ASSERT(SyndromeCheckReverse_A, $countones(error_inject_i) > 0 |-> |syndrome_o) diff --git a/vendor/lowrisc_ip/ip/prim/lint/prim_ram_1p_scr.vlt b/vendor/lowrisc_ip/ip/prim/lint/prim_ram_1p_scr.vlt new file mode 100644 index 00000000..3397d483 --- /dev/null +++ b/vendor/lowrisc_ip/ip/prim/lint/prim_ram_1p_scr.vlt @@ -0,0 +1,9 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +`verilator_config + +// prim_ram_1p_scr +// waive warnings on comparing 'addr_cnt_q' with 32 bit parameters +lint_off -rule WIDTH -file "*/rtl/prim_ram_1p_scr.sv" -match "Operator * expects 32 bits on the LHS, but LHS's VARREF 'addr_cnt_q' generates * bits." diff --git a/vendor/lowrisc_ip/ip/prim/pre_dv/prim_sync_reqack/rtl/prim_sync_reqack_tb.sv b/vendor/lowrisc_ip/ip/prim/pre_dv/prim_sync_reqack/rtl/prim_sync_reqack_tb.sv index 354e6483..81f86d26 100644 --- a/vendor/lowrisc_ip/ip/prim/pre_dv/prim_sync_reqack/rtl/prim_sync_reqack_tb.sv +++ b/vendor/lowrisc_ip/ip/prim/pre_dv/prim_sync_reqack/rtl/prim_sync_reqack_tb.sv @@ -69,6 +69,8 @@ module prim_sync_reqack_tb #( .clk_dst_i (clk_dst), .rst_dst_ni (rst_slow_n), + .req_chk_i (1'b1), + .src_req_i (src_req), .src_ack_o (src_ack), .dst_req_o (dst_req), diff --git a/vendor/lowrisc_ip/ip/prim/prim.core b/vendor/lowrisc_ip/ip/prim/prim.core index 63522fd2..6dc5f674 100644 --- a/vendor/lowrisc_ip/ip/prim/prim.core +++ b/vendor/lowrisc_ip/ip/prim/prim.core @@ -22,14 +22,12 @@ filesets: - lowrisc:prim:arbiter - lowrisc:prim:fifo - lowrisc:prim:alert + - lowrisc:prim:esc - lowrisc:prim:subreg - lowrisc:prim:cipher - lowrisc:prim:xor2 files: - rtl/prim_clock_gating_sync.sv - - rtl/prim_esc_pkg.sv - - rtl/prim_esc_receiver.sv - - rtl/prim_esc_sender.sv - rtl/prim_sram_arbiter.sv - rtl/prim_slicer.sv - rtl/prim_sync_reqack.sv diff --git a/vendor/lowrisc_ip/ip/prim/prim_alert.core b/vendor/lowrisc_ip/ip/prim/prim_alert.core index 01cfdf32..e1befe3d 100644 --- a/vendor/lowrisc_ip/ip/prim/prim_alert.core +++ b/vendor/lowrisc_ip/ip/prim/prim_alert.core @@ -11,6 +11,7 @@ filesets: - lowrisc:prim:assert - lowrisc:prim:diff_decode - lowrisc:prim:buf + - lowrisc:prim:flop files: - rtl/prim_alert_pkg.sv - rtl/prim_alert_receiver.sv diff --git a/vendor/lowrisc_ip/ip/prim/prim_esc.core b/vendor/lowrisc_ip/ip/prim/prim_esc.core new file mode 100644 index 00000000..8b1d83d1 --- /dev/null +++ b/vendor/lowrisc_ip/ip/prim/prim_esc.core @@ -0,0 +1,42 @@ +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:esc" +description: "Escalation send and receive" +filesets: + files_rtl: + depend: + - lowrisc:prim:assert + - lowrisc:prim:diff_decode + - lowrisc:prim:buf + - lowrisc:prim:flop + files: + - rtl/prim_esc_pkg.sv + - rtl/prim_esc_receiver.sv + - rtl/prim_esc_sender.sv + file_type: systemVerilogSource + + files_verilator_waiver: + depend: + # common waivers + - lowrisc:lint:common + + files_ascentlint_waiver: + depend: + # common waivers + - lowrisc:lint:common + + files_veriblelint_waiver: + depend: + # common waivers + - lowrisc:lint:common + +targets: + default: + filesets: + - tool_verilator ? (files_verilator_waiver) + - tool_ascentlint ? (files_ascentlint_waiver) + - tool_veriblelint ? (files_veriblelint_waiver) + - files_rtl diff --git a/vendor/lowrisc_ip/ip/prim/prim_fifo.core b/vendor/lowrisc_ip/ip/prim/prim_fifo.core index 6b97e116..169e247d 100644 --- a/vendor/lowrisc_ip/ip/prim/prim_fifo.core +++ b/vendor/lowrisc_ip/ip/prim/prim_fifo.core @@ -9,6 +9,7 @@ filesets: files_rtl: depend: - lowrisc:prim:assert + - lowrisc:prim:util - lowrisc:prim:flop_2sync files: - rtl/prim_fifo_async.sv diff --git a/vendor/lowrisc_ip/ip/prim/prim_ram_1p_scr.core b/vendor/lowrisc_ip/ip/prim/prim_ram_1p_scr.core index b1563ecd..ebbe055a 100644 --- a/vendor/lowrisc_ip/ip/prim/prim_ram_1p_scr.core +++ b/vendor/lowrisc_ip/ip/prim/prim_ram_1p_scr.core @@ -13,11 +13,22 @@ filesets: - lowrisc:prim:ram_1p_adv - lowrisc:prim:lfsr - lowrisc:prim:all + - lowrisc:prim:util_get_scramble_params + - lowrisc:dv_verilator:memutil_dpi_scrambled files: - rtl/prim_ram_1p_scr.sv file_type: systemVerilogSource + files_verilator_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + - lint/prim_ram_1p_scr.vlt + file_type: vlt + targets: default: filesets: + - tool_verilator ? (files_verilator_waiver) - files_rtl diff --git a/vendor/lowrisc_ip/ip/prim/prim_util_get_scramble_params.core b/vendor/lowrisc_ip/ip/prim/prim_util_get_scramble_params.core new file mode 100644 index 00000000..57dc1ece --- /dev/null +++ b/vendor/lowrisc_ip/ip/prim/prim_util_get_scramble_params.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_get_scramble_params" +description: "DPI functions to snoop scramble key and nonce for simulation" +filesets: + files_rtl: + files: + - rtl/prim_util_get_scramble_params.svh: {is_include_file: true} + file_type: systemVerilogSource + +targets: + default: + filesets: + - files_rtl diff --git a/vendor/lowrisc_ip/ip/prim/rtl/prim_alert_receiver.sv b/vendor/lowrisc_ip/ip/prim/rtl/prim_alert_receiver.sv index bfc1baf0..338387c0 100644 --- a/vendor/lowrisc_ip/ip/prim/rtl/prim_alert_receiver.sv +++ b/vendor/lowrisc_ip/ip/prim/rtl/prim_alert_receiver.sv @@ -56,15 +56,25 @@ module prim_alert_receiver ///////////////////////////////// // decode differential signals // ///////////////////////////////// - logic alert_level, alert_sigint; + logic alert_level, alert_sigint, alert_p, alert_n; + + // This prevents further tool optimizations of the differential signal. + prim_buf #( + .Width(2) + ) u_prim_buf ( + .in_i({alert_tx_i.alert_n, + alert_tx_i.alert_p}), + .out_o({alert_n, + alert_p}) + ); prim_diff_decode #( .AsyncOn(AsyncOn) - ) i_decode_alert ( + ) u_decode_alert ( .clk_i, .rst_ni, - .diff_pi ( alert_tx_i.alert_p ), - .diff_ni ( alert_tx_i.alert_n ), + .diff_pi ( alert_p ), + .diff_ni ( alert_n ), .level_o ( alert_level ), .rise_o ( ), .fall_o ( ), @@ -78,33 +88,43 @@ module prim_alert_receiver typedef enum logic [1:0] {Idle, HsAckWait, Pause0, Pause1} state_e; state_e state_d, state_q; logic ping_rise; - logic ping_tog, ping_tog_dp, ping_tog_qp, ping_tog_dn, ping_tog_qn; - logic ack, ack_dp, ack_qp, ack_dn, ack_qn; + logic ping_tog_pd, ping_tog_pq, ping_tog_dn, ping_tog_nq; + logic ack_pd, ack_pq, ack_dn, ack_nq; logic ping_req_d, ping_req_q; logic ping_pending_d, ping_pending_q; // signal ping request upon positive transition on ping_req_i // signalling is performed by a level change event on the diff output assign ping_req_d = ping_req_i; - assign ping_rise = ping_req_i && !ping_req_q; - assign ping_tog = (ping_rise) ? ~ping_tog_qp : ping_tog_qp; + assign ping_rise = ping_req_i && !ping_req_q; + assign ping_tog_pd = (ping_rise) ? ~ping_tog_pq : ping_tog_pq; + + assign ack_dn = ~ack_pd; + assign ping_tog_dn = ~ping_tog_pd; // This prevents further tool optimizations of the differential signal. - prim_buf u_prim_buf_ack_p ( - .in_i(ack), - .out_o(ack_dp) + prim_generic_flop #( + .Width (2), + .ResetValue(2'b10) + ) u_prim_generic_flop_ack ( + .clk_i, + .rst_ni, + .d_i({ack_dn, + ack_pd}), + .q_o({ack_nq, + ack_pq}) ); - prim_buf u_prim_buf_ack_n ( - .in_i(~ack), - .out_o(ack_dn) - ); - prim_buf u_prim_buf_ping_p ( - .in_i(ping_tog), - .out_o(ping_tog_dp) - ); - prim_buf u_prim_buf_ping_n ( - .in_i(~ping_tog), - .out_o(ping_tog_dn) + + prim_generic_flop #( + .Width (2), + .ResetValue(2'b10) + ) u_prim_generic_flop_ping ( + .clk_i, + .rst_ni, + .d_i({ping_tog_dn, + ping_tog_pd}), + .q_o({ping_tog_nq, + ping_tog_pq}) ); // the ping pending signal is used to in the FSM to distinguish whether the @@ -115,11 +135,11 @@ module prim_alert_receiver assign ping_pending_d = ping_rise | ((~ping_ok_o) & ping_req_i & ping_pending_q); // diff pair outputs - assign alert_rx_o.ack_p = ack_qp; - assign alert_rx_o.ack_n = ack_qn; + assign alert_rx_o.ack_p = ack_pq; + assign alert_rx_o.ack_n = ack_nq; - assign alert_rx_o.ping_p = ping_tog_qp; - assign alert_rx_o.ping_n = ping_tog_qn; + assign alert_rx_o.ping_p = ping_tog_pq; + assign alert_rx_o.ping_n = ping_tog_nq; // this FSM receives the four phase handshakes from the alert receiver // note that the latency of the alert_p/n input diff pair is at least one @@ -128,7 +148,7 @@ module prim_alert_receiver always_comb begin : p_fsm // default state_d = state_q; - ack = 1'b0; + ack_pd = 1'b0; ping_ok_o = 1'b0; integ_fail_o = 1'b0; alert_o = 1'b0; @@ -138,7 +158,7 @@ module prim_alert_receiver // wait for handshake to be initiated if (alert_level) begin state_d = HsAckWait; - ack = 1'b1; + ack_pd = 1'b1; // signal either an alert or ping received on the output if (ping_pending_q) begin ping_ok_o = 1'b1; @@ -152,7 +172,7 @@ module prim_alert_receiver if (!alert_level) begin state_d = Pause0; end else begin - ack = 1'b1; + ack_pd = 1'b1; end end // pause cycles between back-to-back handshakes @@ -164,7 +184,7 @@ module prim_alert_receiver // override in case of sigint if (alert_sigint) begin state_d = Idle; - ack = 1'b0; + ack_pd = 1'b0; ping_ok_o = 1'b0; integ_fail_o = 1'b1; alert_o = 1'b0; @@ -174,18 +194,10 @@ module prim_alert_receiver always_ff @(posedge clk_i or negedge rst_ni) begin : p_reg if (!rst_ni) begin state_q <= Idle; - ack_qp <= 1'b0; - ack_qn <= 1'b1; - ping_tog_qp <= 1'b0; - ping_tog_qn <= 1'b1; ping_req_q <= 1'b0; ping_pending_q <= 1'b0; end else begin state_q <= state_d; - ack_qp <= ack_dp; - ack_qn <= ack_dn; - ping_tog_qp <= ping_tog_dp; - ping_tog_qn <= ping_tog_dn; ping_req_q <= ping_req_d; ping_pending_q <= ping_pending_d; end diff --git a/vendor/lowrisc_ip/ip/prim/rtl/prim_alert_sender.sv b/vendor/lowrisc_ip/ip/prim/rtl/prim_alert_sender.sv index c1b385b7..f3d7d5bc 100644 --- a/vendor/lowrisc_ip/ip/prim/rtl/prim_alert_sender.sv +++ b/vendor/lowrisc_ip/ip/prim/rtl/prim_alert_sender.sv @@ -69,36 +69,56 @@ module prim_alert_sender ///////////////////////////////// // decode differential signals // ///////////////////////////////// - logic ping_sigint, ping_event; + logic ping_sigint, ping_event, ping_n, ping_p; - prim_diff_decode #( - .AsyncOn(AsyncOn) - ) i_decode_ping ( - .clk_i, - .rst_ni, - .diff_pi ( alert_rx_i.ping_p ), - .diff_ni ( alert_rx_i.ping_n ), - .level_o ( ), - .rise_o ( ), - .fall_o ( ), - .event_o ( ping_event ), - .sigint_o ( ping_sigint ) + // This prevents further tool optimizations of the differential signal. + prim_buf #( + .Width(2) + ) u_prim_buf_ping ( + .in_i({alert_rx_i.ping_n, + alert_rx_i.ping_p}), + .out_o({ping_n, + ping_p}) ); - logic ack_sigint, ack_level; + prim_diff_decode #( + .AsyncOn(AsyncOn) + ) u_decode_ping ( + .clk_i, + .rst_ni, + .diff_pi ( ping_p ), + .diff_ni ( ping_n ), + .level_o ( ), + .rise_o ( ), + .fall_o ( ), + .event_o ( ping_event ), + .sigint_o ( ping_sigint ) + ); + + logic ack_sigint, ack_level, ack_n, ack_p; + + // This prevents further tool optimizations of the differential signal. + prim_buf #( + .Width(2) + ) u_prim_buf_ack ( + .in_i({alert_rx_i.ack_n, + alert_rx_i.ack_p}), + .out_o({ack_n, + ack_p}) + ); prim_diff_decode #( .AsyncOn(AsyncOn) - ) i_decode_ack ( + ) u_decode_ack ( .clk_i, .rst_ni, - .diff_pi ( alert_rx_i.ack_p ), - .diff_ni ( alert_rx_i.ack_n ), - .level_o ( ack_level ), - .rise_o ( ), - .fall_o ( ), - .event_o ( ), - .sigint_o ( ack_sigint ) + .diff_pi ( ack_p ), + .diff_ni ( ack_n ), + .level_o ( ack_level ), + .rise_o ( ), + .fall_o ( ), + .event_o ( ), + .sigint_o ( ack_sigint ) ); @@ -116,7 +136,7 @@ module prim_alert_sender Pause1 } state_e; state_e state_d, state_q; - logic alert_p, alert_n, alert_pq, alert_nq, alert_pd, alert_nd; + logic alert_pq, alert_nq, alert_pd, alert_nd; logic sigint_detected; assign sigint_detected = ack_sigint | ping_sigint; @@ -164,8 +184,8 @@ module prim_alert_sender always_comb begin : p_fsm // default state_d = state_q; - alert_p = 1'b0; - alert_n = 1'b1; + alert_pd = 1'b0; + alert_nd = 1'b1; ping_clr = 1'b0; alert_clr = 1'b0; @@ -174,8 +194,8 @@ module prim_alert_sender // alert always takes precedence if (alert_trigger || ping_trigger) begin state_d = (alert_trigger) ? AlertHsPhase1 : PingHsPhase1; - alert_p = 1'b1; - alert_n = 1'b0; + alert_pd = 1'b1; + alert_nd = 1'b0; end end // waiting for ack from receiver @@ -183,8 +203,8 @@ module prim_alert_sender if (ack_level) begin state_d = AlertHsPhase2; end else begin - alert_p = 1'b1; - alert_n = 1'b0; + alert_pd = 1'b1; + alert_nd = 1'b0; end end // wait for deassertion of ack @@ -199,8 +219,8 @@ module prim_alert_sender if (ack_level) begin state_d = PingHsPhase2; end else begin - alert_p = 1'b1; - alert_n = 1'b0; + alert_pd = 1'b1; + alert_nd = 1'b0; end end // wait for deassertion of ack @@ -229,8 +249,8 @@ module prim_alert_sender state_d = Idle; if (sigint_detected) begin state_d = SigInt; - alert_p = ~alert_pq; - alert_n = ~alert_pq; + alert_pd = ~alert_pq; + alert_nd = ~alert_pq; end end // catch parasitic states @@ -239,35 +259,32 @@ module prim_alert_sender // bail out if a signal integrity issue has been detected if (sigint_detected && (state_q != SigInt)) begin state_d = SigInt; - alert_p = 1'b0; - alert_n = 1'b0; + alert_pd = 1'b0; + alert_nd = 1'b0; ping_clr = 1'b0; alert_clr = 1'b0; end end // This prevents further tool optimizations of the differential signal. - prim_buf u_prim_buf_p ( - .in_i(alert_p), - .out_o(alert_pd) - ); - prim_buf u_prim_buf_n ( - .in_i(alert_n), - .out_o(alert_nd) + prim_generic_flop #( + .Width (2), + .ResetValue(2'b10) + ) u_prim_generic_flop ( + .clk_i, + .rst_ni, + .d_i({alert_nd, alert_pd}), + .q_o({alert_nq, alert_pq}) ); always_ff @(posedge clk_i or negedge rst_ni) begin : p_reg if (!rst_ni) begin state_q <= Idle; - alert_pq <= 1'b0; - alert_nq <= 1'b1; alert_set_q <= 1'b0; alert_test_set_q <= 1'b0; ping_set_q <= 1'b0; end else begin state_q <= state_d; - alert_pq <= alert_pd; - alert_nq <= alert_nd; alert_set_q <= alert_set_d; alert_test_set_q <= alert_test_set_d; ping_set_q <= ping_set_d; diff --git a/vendor/lowrisc_ip/ip/prim/rtl/prim_arbiter_ppc.sv b/vendor/lowrisc_ip/ip/prim/rtl/prim_arbiter_ppc.sv index 86bef1e9..17f3e19a 100644 --- a/vendor/lowrisc_ip/ip/prim/rtl/prim_arbiter_ppc.sv +++ b/vendor/lowrisc_ip/ip/prim/rtl/prim_arbiter_ppc.sv @@ -8,7 +8,6 @@ // N: Number of request ports // DW: Data width // DataPort: Set to 1 to enable the data port. Otherwise that port will be ignored. -// EnReqStabA: Checks whether requests remain asserted until granted // // This is the original implementation of the arbiter which relies on parallel prefix computing // optimization to optimize the request / arbiter tree. Not all synthesis tools may support this. @@ -18,8 +17,8 @@ // this behavior. // // Also, this module contains a request stability assertion that checks that requests stay asserted -// until they have been served. This assertion can be optionally disabled by setting EnReqStabA to -// zero. This is a non-functional parameter and does not affect the designs behavior. +// until they have been served. This assertion can be gated by driving the req_chk_i low. This is +// a non-functional input and does not affect the designs behavior. // // See also: prim_arbiter_tree @@ -33,15 +32,14 @@ module prim_arbiter_ppc #( // EnDataPort: {0, 1}, if 0, input data will be ignored parameter bit EnDataPort = 1, - // Non-functional parameter to switch on the request stability assertion - parameter bit EnReqStabA = 1, - // Derived parameters localparam int IdxW = $clog2(N) ) ( input clk_i, input rst_ni, + input req_chk_i, // Used for gating assertions. Drive to 1 during normal + // operation. input [ N-1:0] req_i, input [DW-1:0] data_i [N], output logic [ N-1:0] gnt_o, @@ -52,6 +50,10 @@ module prim_arbiter_ppc #( input ready_i ); + // req_chk_i is used for gating assertions only. + logic unused_req_chk; + assign unused_req_chk = req_chk_i; + `ASSERT_INIT(CheckNGreaterZero_A, N > 0) // this case is basically just a bypass @@ -165,13 +167,12 @@ if (EnDataPort) begin: gen_data_port_assertion `ASSERT(DataFlow_A, ready_i && valid_o |-> data_o == data_i[idx_o]) end -if (EnReqStabA) begin : gen_lock_assertion // requests must stay asserted until they have been granted - `ASSUME(ReqStaysHighUntilGranted0_M, (|req_i) && !ready_i |=> - (req_i & $past(req_i)) == $past(req_i)) + `ASSUME(ReqStaysHighUntilGranted0_M, |req_i && !ready_i |=> + (req_i & $past(req_i)) == $past(req_i), clk_i, !rst_ni || !req_chk_i) // check that the arbitration decision is held if the sink is not ready - `ASSERT(LockArbDecision_A, |req_i && !ready_i |=> idx_o == $past(idx_o)) -end + `ASSERT(LockArbDecision_A, |req_i && !ready_i |=> idx_o == $past(idx_o), + clk_i, !rst_ni || !req_chk_i) // FPV-only assertions with symbolic variables `ifdef FPV_ON @@ -216,11 +217,9 @@ end end end - if (EnReqStabA) begin : gen_lock_assertion_fpv - // requests must stay asserted until they have been granted - `ASSUME(ReqStaysHighUntilGranted1_M, req_i[k] & !gnt_o[k] |=> - req_i[k], clk_i, !rst_ni) - end + // requests must stay asserted until they have been granted + `ASSUME(ReqStaysHighUntilGranted1_M, req_i[k] && !gnt_o[k] |=> + req_i[k], clk_i, !rst_ni || !req_chk_i) `endif endmodule : prim_arbiter_ppc diff --git a/vendor/lowrisc_ip/ip/prim/rtl/prim_arbiter_tree.sv b/vendor/lowrisc_ip/ip/prim/rtl/prim_arbiter_tree.sv index 992bc2ff..93d809e9 100644 --- a/vendor/lowrisc_ip/ip/prim/rtl/prim_arbiter_tree.sv +++ b/vendor/lowrisc_ip/ip/prim/rtl/prim_arbiter_tree.sv @@ -8,7 +8,6 @@ // N: Number of request ports // DW: Data width // DataPort: Set to 1 to enable the data port. Otherwise that port will be ignored. -// EnReqStabA: Checks whether requests remain asserted until granted // // This is a tree implementation of a round robin arbiter. It has the same behavior as the PPC // implementation in prim_arbiter_ppc, and also uses a prefix summing approach to determine the next @@ -25,8 +24,8 @@ // this behavior. // // Also, this module contains a request stability assertion that checks that requests stay asserted -// until they have been served. This assertion can be optionally disabled by setting EnReqStabA to -// zero. This is a non-functional parameter and does not affect the designs behavior. +// until they have been served. This assertion can be gated by driving the req_chk_i low. This is +// a non-functional input and does not affect the designs behavior. // // See also: prim_arbiter_ppc @@ -40,15 +39,14 @@ module prim_arbiter_tree #( // EnDataPort: {0, 1}, if 0, input data will be ignored parameter bit EnDataPort = 1, - // Non-functional parameter to switch on the request stability assertion - parameter bit EnReqStabA = 1, - // Derived parameters localparam int IdxW = $clog2(N) ) ( input clk_i, input rst_ni, + input req_chk_i, // Used for gating assertions. Drive to 1 during normal + // operation. input [ N-1:0] req_i, input [DW-1:0] data_i [N], output logic [ N-1:0] gnt_o, @@ -59,6 +57,10 @@ module prim_arbiter_tree #( input ready_i ); + // req_chk_i is used for gating assertions only. + logic unused_req_chk; + assign unused_req_chk = req_chk_i; + `ASSERT_INIT(CheckNGreaterZero_A, N > 0) // this case is basically just a bypass @@ -140,25 +142,26 @@ module prim_arbiter_tree #( end else begin : gen_nodes // local helper variable logic sel; - always_comb begin : p_node - // forward path (requests and data) - // each node looks at its two children, and selects the one with higher priority - sel = ~req_tree[C0] | ~prio_tree[C0] & prio_tree[C1]; - // propagate requests - req_tree[Pa] = req_tree[C0] | req_tree[C1]; - prio_tree[Pa] = prio_tree[C1] | prio_tree[C0]; - // data and index muxes - idx_tree[Pa] = (sel) ? idx_tree[C1] : idx_tree[C0]; - data_tree[Pa] = (sel) ? data_tree[C1] : data_tree[C0]; - // backward path (grants and prefix sum) - // this propagates the selction index back and computes a hot one mask - sel_tree[C0] = sel_tree[Pa] & ~sel; - sel_tree[C1] = sel_tree[Pa] & sel; - // this performs a prefix sum for masking the input requests in the next cycle - mask_tree[C0] = mask_tree[Pa]; - mask_tree[C1] = mask_tree[Pa] | sel_tree[C0]; - end + // forward path (requests and data) + // each node looks at its two children, and selects the one with higher priority + assign sel = ~req_tree[C0] | ~prio_tree[C0] & prio_tree[C1]; + // propagate requests + assign req_tree[Pa] = req_tree[C0] | req_tree[C1]; + assign prio_tree[Pa] = prio_tree[C1] | prio_tree[C0]; + // data and index muxes + // Note: these ternaries have triggered a synthesis bug in Vivado versions older + // than 2020.2. If the problem resurfaces again, have a look at issue #1408. + assign idx_tree[Pa] = (sel) ? idx_tree[C1] : idx_tree[C0]; + assign data_tree[Pa] = (sel) ? data_tree[C1] : data_tree[C0]; + + // backward path (grants and prefix sum) + // this propagates the selction index back and computes a hot one mask + assign sel_tree[C0] = sel_tree[Pa] & ~sel; + assign sel_tree[C1] = sel_tree[Pa] & sel; + // this performs a prefix sum for masking the input requests in the next cycle + assign mask_tree[C0] = mask_tree[Pa]; + assign mask_tree[C1] = mask_tree[Pa] | sel_tree[C0]; end end : gen_level end : gen_tree @@ -230,13 +233,12 @@ if (EnDataPort) begin: gen_data_port_assertion `ASSERT(DataFlow_A, ready_i && valid_o |-> data_o == data_i[idx_o]) end -if (EnReqStabA) begin : gen_lock_assertion // requests must stay asserted until they have been granted - `ASSUME(ReqStaysHighUntilGranted0_M, (|req_i) && !ready_i |=> - (req_i & $past(req_i)) == $past(req_i)) + `ASSUME(ReqStaysHighUntilGranted0_M, |req_i && !ready_i |=> + (req_i & $past(req_i)) == $past(req_i), clk_i, !rst_ni || !req_chk_i) // check that the arbitration decision is held if the sink is not ready - `ASSERT(LockArbDecision_A, |req_i && !ready_i |=> idx_o == $past(idx_o)) -end + `ASSERT(LockArbDecision_A, |req_i && !ready_i |=> idx_o == $past(idx_o), + clk_i, !rst_ni || !req_chk_i) // FPV-only assertions with symbolic variables `ifdef FPV_ON @@ -281,11 +283,9 @@ end end end - if (EnReqStabA) begin : gen_lock_assertion_fpv - // requests must stay asserted until they have been granted - `ASSUME(ReqStaysHighUntilGranted1_M, req_i[k] & !gnt_o[k] |=> - req_i[k], clk_i, !rst_ni) - end + // requests must stay asserted until they have been granted + `ASSUME(ReqStaysHighUntilGranted1_M, req_i[k] && !gnt_o[k] |=> + req_i[k], clk_i, !rst_ni || !req_chk_i) `endif endmodule : prim_arbiter_tree diff --git a/vendor/lowrisc_ip/ip/prim/rtl/prim_edn_req.sv b/vendor/lowrisc_ip/ip/prim/rtl/prim_edn_req.sv index e973fccd..ad5b2ec2 100644 --- a/vendor/lowrisc_ip/ip/prim/rtl/prim_edn_req.sv +++ b/vendor/lowrisc_ip/ip/prim/rtl/prim_edn_req.sv @@ -16,15 +16,13 @@ module prim_edn_req import prim_alert_pkg::*; #( - parameter int OutWidth = 32, - - // Non-functional parameter to switch on the request stability assertion. - // Used in submodule `prim_sync_reqack`. - parameter bit EnReqStabA = 1 + parameter int OutWidth = 32 ) ( // Design side input clk_i, input rst_ni, + input req_chk_i, // Used for gating assertions. Drive to 1 during normal + // operation. input req_i, output logic ack_o, output logic [OutWidth-1:0] data_o, @@ -46,13 +44,13 @@ module prim_edn_req prim_sync_reqack_data #( .Width(SyncWidth), .DataSrc2Dst(1'b0), - .DataReg(1'b0), - .EnReqStabA(EnReqStabA) + .DataReg(1'b0) ) u_prim_sync_reqack_data ( .clk_src_i ( clk_i ), .rst_src_ni ( rst_ni ), .clk_dst_i ( clk_edn_i ), .rst_dst_ni ( rst_edn_ni ), + .req_chk_i ( req_chk_i ), .src_req_i ( word_req ), .src_ack_o ( word_ack ), .dst_req_o ( edn_o.edn_req ), diff --git a/vendor/lowrisc_ip/ip/prim/rtl/prim_esc_receiver.sv b/vendor/lowrisc_ip/ip/prim/rtl/prim_esc_receiver.sv index 2ffe702c..775c710c 100644 --- a/vendor/lowrisc_ip/ip/prim/rtl/prim_esc_receiver.sv +++ b/vendor/lowrisc_ip/ip/prim/rtl/prim_esc_receiver.sv @@ -20,7 +20,32 @@ module prim_esc_receiver import prim_esc_pkg::*; -( +#( + // The number of escalation severities. Should be set to the Alert Handler's N_ESC_SEV when this + // primitive is instantiated. + parameter int N_ESC_SEV = 4, + + // The width of the Alert Handler's ping counter. Should be set to the Alert Handler's PING_CNT_DW + // when this primitive is instantiated. + parameter int PING_CNT_DW = 16, + + // This counter monitors incoming ping requests and auto-escalates if the alert handler + // ceases to send them regularly. The maximum number of cycles between subsequent ping requests + // is N_ESC_SEV x (2 x 2 x 2**PING_CNT_DW), see also implementation of the ping timer + // (alert_handler_ping_timer.sv). The timeout counter below uses a timeout that is 4x larger than + // that in order to incorporate some margin. + // + // Do NOT modify this counter value, when instantiating it in the design. It is only exposed to + // reduce the state space in the FPV testbench. + localparam int MarginFactor = 4, + localparam int NumWaitCounts = 2, + localparam int NumTimeoutCounts = 2, + parameter int TimeoutCntDw = $clog2(MarginFactor) + + $clog2(N_ESC_SEV) + + $clog2(NumWaitCounts) + + $clog2(NumTimeoutCounts) + + PING_CNT_DW +) ( input clk_i, input rst_ni, // escalation enable @@ -35,15 +60,25 @@ module prim_esc_receiver // decode differential signals // ///////////////////////////////// - logic esc_level, sigint_detected; + logic esc_level, esc_p, esc_n, sigint_detected; + + // This prevents further tool optimizations of the differential signal. + prim_buf #( + .Width(2) + ) u_prim_buf_esc ( + .in_i({esc_tx_i.esc_n, + esc_tx_i.esc_p}), + .out_o({esc_n, + esc_p}) + ); prim_diff_decode #( .AsyncOn(1'b0) - ) i_decode_esc ( + ) u_decode_esc ( .clk_i, .rst_ni, - .diff_pi ( esc_tx_i.esc_p ), - .diff_ni ( esc_tx_i.esc_n ), + .diff_pi ( esc_p ), + .diff_ni ( esc_n ), .level_o ( esc_level ), .rise_o ( ), .fall_o ( ), @@ -51,23 +86,59 @@ module prim_esc_receiver .sigint_o ( sigint_detected ) ); + //////////////////////////////////////////// + // Ping Monitor Counter / Auto Escalation // + //////////////////////////////////////////// + + logic ping_en, esc_en; + logic [1:0][TimeoutCntDw-1:0] cnt_q; + + for (genvar k = 0; k < 2; k++) begin : gen_timeout_cnt + + logic [TimeoutCntDw-1:0] cnt_d; + + // The timeout counter is kicked off when the first ping occurs, and subsequent pings reset + // the counter to 1. The counter keeps on counting when it is nonzero, and saturates when it + // has reached its maximum (this state is terminal). + assign cnt_d = (ping_en && !(&cnt_q[k])) ? TimeoutCntDw'(1) : + ((cnt_q[k] > '0) && !(&cnt_q[k])) ? cnt_q[k] + 1'b1 : + cnt_q[k]; + prim_flop #( + .Width(TimeoutCntDw) + ) u_prim_flop ( + .clk_i, + .rst_ni, + .d_i(cnt_d), + .q_o(cnt_q[k]) + ); + end + + // Escalation is asserted if + // - requested via the escalation sender/receiver path, + // - the ping monitor timeout is reached, + // - the two ping monitor counters are in an inconsistent state. + assign esc_en_o = esc_en || + (&cnt_q[0]) || + (cnt_q[0] != cnt_q[1]); + ///////////////// // RX/TX Logic // ///////////////// typedef enum logic [2:0] {Idle, Check, PingResp, EscResp, SigInt} state_e; state_e state_d, state_q; - logic resp_p, resp_pd, resp_pq; - logic resp_n, resp_nd, resp_nq; + logic resp_pd, resp_pq; + logic resp_nd, resp_nq; // This prevents further tool optimizations of the differential signal. - prim_buf u_prim_buf_p ( - .in_i(resp_p), - .out_o(resp_pd) - ); - prim_buf u_prim_buf_n ( - .in_i(resp_n), - .out_o(resp_nd) + prim_generic_flop #( + .Width(2), + .ResetValue(2'b10) + ) u_prim_generic_flop ( + .clk_i, + .rst_ni, + .d_i({resp_nd, resp_pd}), + .q_o({resp_nq, resp_pq}) ); assign esc_rx_o.resp_p = resp_pq; @@ -76,17 +147,18 @@ module prim_esc_receiver always_comb begin : p_fsm // default state_d = state_q; - resp_p = 1'b0; - resp_n = 1'b1; - esc_en_o = 1'b0; + resp_pd = 1'b0; + resp_nd = 1'b1; + esc_en = 1'b0; + ping_en = 1'b0; unique case (state_q) // wait for the esc_p/n diff pair Idle: begin if (esc_level) begin state_d = Check; - resp_p = 1'b1; - resp_n = 1'b0; + resp_pd = 1'b1; + resp_nd = 1'b0; end end // we decide here whether this is only a ping request or @@ -95,18 +167,19 @@ module prim_esc_receiver state_d = PingResp; if (esc_level) begin state_d = EscResp; - esc_en_o = 1'b1; + esc_en = 1'b1; end end // finish ping response. in case esc_level is again asserted, // we got an escalation signal (pings cannot occur back to back) PingResp: begin state_d = Idle; - resp_p = 1'b1; - resp_n = 1'b0; + resp_pd = 1'b1; + resp_nd = 1'b0; + ping_en = 1'b1; if (esc_level) begin state_d = EscResp; - esc_en_o = 1'b1; + esc_en = 1'b1; end end // we have got an escalation enable pulse, @@ -115,9 +188,9 @@ module prim_esc_receiver state_d = Idle; if (esc_level) begin state_d = EscResp; - resp_p = ~resp_pq; - resp_n = resp_pq; - esc_en_o = 1'b1; + resp_pd = ~resp_pq; + resp_nd = resp_pq; + esc_en = 1'b1; end end // we have a signal integrity issue at one of @@ -129,8 +202,8 @@ module prim_esc_receiver state_d = Idle; if (sigint_detected) begin state_d = SigInt; - resp_p = ~resp_pq; - resp_n = ~resp_pq; + resp_pd = ~resp_pq; + resp_nd = ~resp_pq; end end default : state_d = Idle; @@ -139,8 +212,8 @@ module prim_esc_receiver // bail out if a signal integrity issue has been detected if (sigint_detected && (state_q != SigInt)) begin state_d = SigInt; - resp_p = 1'b0; - resp_n = 1'b0; + resp_pd = 1'b0; + resp_nd = 1'b0; end end @@ -152,12 +225,8 @@ module prim_esc_receiver always_ff @(posedge clk_i or negedge rst_ni) begin : p_regs if (!rst_ni) begin state_q <= Idle; - resp_pq <= 1'b0; - resp_nq <= 1'b1; end else begin state_q <= state_d; - resp_pq <= resp_pd; - resp_nq <= resp_nd; end end @@ -186,5 +255,9 @@ module prim_esc_receiver // detect escalation pulse `ASSERT(EscEnCheck_A, esc_tx_i.esc_p && (esc_tx_i.esc_p ^ esc_tx_i.esc_n) && state_q != SigInt |=> esc_tx_i.esc_p && (esc_tx_i.esc_p ^ esc_tx_i.esc_n) |-> esc_en_o) + // make sure the counter does not wrap around + `ASSERT(EscCntWrap_A, &cnt_q[0] |=> cnt_q[0] != 0) + // if the counter expires, escalation should be asserted + `ASSERT(EscCntEsc_A, &cnt_q[0] |-> esc_en_o) endmodule : prim_esc_receiver diff --git a/vendor/lowrisc_ip/ip/prim/rtl/prim_esc_sender.sv b/vendor/lowrisc_ip/ip/prim/rtl/prim_esc_sender.sv index a7384206..aae622ed 100644 --- a/vendor/lowrisc_ip/ip/prim/rtl/prim_esc_sender.sv +++ b/vendor/lowrisc_ip/ip/prim/rtl/prim_esc_sender.sv @@ -43,15 +43,25 @@ module prim_esc_sender // decode differential signals // ///////////////////////////////// - logic resp, sigint_detected; + logic resp, resp_n, resp_p, sigint_detected; + + // This prevents further tool optimizations of the differential signal. + prim_buf #( + .Width(2) + ) u_prim_buf_resp ( + .in_i({esc_rx_i.resp_n, + esc_rx_i.resp_p}), + .out_o({resp_n, + resp_p}) + ); prim_diff_decode #( .AsyncOn(1'b0) - ) i_decode_resp ( + ) u_decode_resp ( .clk_i, .rst_ni, - .diff_pi ( esc_rx_i.resp_p ), - .diff_ni ( esc_rx_i.resp_n ), + .diff_pi ( resp_p ), + .diff_ni ( resp_n ), .level_o ( resp ), .rise_o ( ), .fall_o ( ), @@ -75,13 +85,13 @@ module prim_esc_sender assign esc_p = esc_req_i | esc_req_q | (ping_req_d & ~ping_req_q); // This prevents further tool optimizations of the differential signal. - prim_buf u_prim_buf_p ( - .in_i(esc_p), - .out_o(esc_tx_o.esc_p) - ); - prim_buf u_prim_buf_n ( - .in_i(~esc_p), - .out_o(esc_tx_o.esc_n) + prim_buf #( + .Width(2) + ) u_prim_buf_esc ( + .in_i({~esc_p, + esc_p}), + .out_o({esc_tx_o.esc_n, + esc_tx_o.esc_p}) ); ////////////// diff --git a/vendor/lowrisc_ip/ip/prim/rtl/prim_fifo_async.sv b/vendor/lowrisc_ip/ip/prim/rtl/prim_fifo_async.sv index 8da084fd..97fbc581 100644 --- a/vendor/lowrisc_ip/ip/prim/rtl/prim_fifo_async.sv +++ b/vendor/lowrisc_ip/ip/prim/rtl/prim_fifo_async.sv @@ -52,7 +52,7 @@ module prim_fifo_async #( assign fifo_incr_wptr = wvalid_i & wready_o; // decimal version - assign fifo_wptr_d = fifo_wptr_q + PTR_WIDTH'(1); + assign fifo_wptr_d = fifo_wptr_q + PTR_WIDTH'(1'b1); always_ff @(posedge clk_wr_i or negedge rst_wr_ni) begin if (!rst_wr_ni) begin @@ -85,7 +85,7 @@ module prim_fifo_async #( assign fifo_incr_rptr = rvalid_o & rready_i; // decimal version - assign fifo_rptr_d = fifo_rptr_q + PTR_WIDTH'(1); + assign fifo_rptr_d = fifo_rptr_q + PTR_WIDTH'(1'b1); always_ff @(posedge clk_rd_i or negedge rst_rd_ni) begin if (!rst_rd_ni) begin @@ -124,8 +124,10 @@ module prim_fifo_async #( // Empty / Full // ////////////////// - assign full_wclk = (fifo_wptr_q == (fifo_rptr_sync_q ^ {1'b1,{(PTR_WIDTH-1){1'b0}}})); - assign full_rclk = (fifo_wptr_sync_combi == (fifo_rptr_q ^ {1'b1,{(PTR_WIDTH-1){1'b0}}})); + logic [PTR_WIDTH-1:0] xor_mask; + assign xor_mask = PTR_WIDTH'(1'b1) << (PTR_WIDTH-1); + assign full_wclk = (fifo_wptr_q == (fifo_rptr_sync_q ^ xor_mask)); + assign full_rclk = (fifo_wptr_sync_combi == (fifo_rptr_q ^ xor_mask)); assign empty_rclk = (fifo_wptr_sync_combi == fifo_rptr_q); if (Depth > 1) begin : g_depth_calc @@ -210,32 +212,42 @@ module prim_fifo_async #( function automatic [PTR_WIDTH-1:0] dec2gray(input logic [PTR_WIDTH-1:0] decval); logic [PTR_WIDTH-1:0] decval_sub; - logic [PTR_WIDTH-2:0] decval_in; + logic [PTR_WIDTH-1:0] decval_in; logic unused_decval_msb; decval_sub = (PTR_WIDTH)'(Depth) - {1'b0, decval[PTR_WIDTH-2:0]} - 1'b1; - {unused_decval_msb, decval_in} = decval[PTR_WIDTH-1] ? decval_sub : decval; - // Was done in two assigns for low bits and top bit - // but that generates a (bogus) verilator warning, so do in one assign - dec2gray = {decval[PTR_WIDTH-1], - {1'b0,decval_in[PTR_WIDTH-2:1]} ^ decval_in[PTR_WIDTH-2:0]}; + decval_in = decval[PTR_WIDTH-1] ? decval_sub : decval; + + // We do not care about the MSB, hence we mask it out + unused_decval_msb = decval_in[PTR_WIDTH-1]; + decval_in[PTR_WIDTH-1] = 1'b0; + + // Perform the XOR conversion + dec2gray = decval_in; + dec2gray ^= (decval_in >> 1); + + // Override the MSB + dec2gray[PTR_WIDTH-1] = decval[PTR_WIDTH-1]; endfunction // Algorithm walks up from 0..N-1 then flips the upper bit and walks down from N-1 to 0. function automatic [PTR_WIDTH-1:0] gray2dec(input logic [PTR_WIDTH-1:0] grayval); - logic [PTR_WIDTH-2:0] dec_tmp, dec_tmp_sub; + logic [PTR_WIDTH-1:0] dec_tmp, dec_tmp_sub; logic unused_decsub_msb; - dec_tmp[PTR_WIDTH-2] = grayval[PTR_WIDTH-2]; - for (int i = PTR_WIDTH-3; i >= 0; i--) begin + dec_tmp = '0; + for (int i = PTR_WIDTH-2; i >= 0; i--) begin dec_tmp[i] = dec_tmp[i+1] ^ grayval[i]; end - {unused_decsub_msb, dec_tmp_sub} = (PTR_WIDTH-1)'(Depth) - {1'b0, dec_tmp} - 1'b1; + dec_tmp_sub = (PTR_WIDTH)'(Depth) - dec_tmp - 1'b1; if (grayval[PTR_WIDTH-1]) begin - gray2dec = {1'b1, dec_tmp_sub}; + gray2dec = dec_tmp_sub; + // Override MSB + gray2dec[PTR_WIDTH-1] = 1'b1; + unused_decsub_msb = dec_tmp_sub[PTR_WIDTH-1]; end else begin - gray2dec = {1'b0, dec_tmp}; + gray2dec = dec_tmp; end endfunction @@ -250,7 +262,7 @@ module prim_fifo_async #( end else if (Depth == 2) begin : g_simple_gray_conversion assign fifo_rptr_sync_combi = {fifo_rptr_gray_sync[PTR_WIDTH-1], ^fifo_rptr_gray_sync}; - assign fifo_wptr_sync_combi = {fifo_wptr_gray_sync[PTR_WIDTH-1], ^fifo_rptr_gray_sync}; + assign fifo_wptr_sync_combi = {fifo_wptr_gray_sync[PTR_WIDTH-1], ^fifo_wptr_gray_sync}; assign fifo_rptr_gray_d = {fifo_rptr_d[PTR_WIDTH-1], ^fifo_rptr_d}; assign fifo_wptr_gray_d = {fifo_wptr_d[PTR_WIDTH-1], ^fifo_wptr_d}; diff --git a/vendor/lowrisc_ip/ip/prim/rtl/prim_lfsr.sv b/vendor/lowrisc_ip/ip/prim/rtl/prim_lfsr.sv index 73e18b4e..be43175e 100644 --- a/vendor/lowrisc_ip/ip/prim/rtl/prim_lfsr.sv +++ b/vendor/lowrisc_ip/ip/prim/rtl/prim_lfsr.sv @@ -354,6 +354,9 @@ module prim_lfsr #( // Unknown // ///////////// end else begin : gen_unknown_type + assign coeffs = '0; + assign next_lfsr_state = '0; + assign lockup = 1'b0; `ASSERT_INIT(UnknownLfsrType_A, 0) end diff --git a/vendor/lowrisc_ip/ip/prim/rtl/prim_ram_1p_scr.sv b/vendor/lowrisc_ip/ip/prim/rtl/prim_ram_1p_scr.sv index db767191..3f38f106 100644 --- a/vendor/lowrisc_ip/ip/prim/rtl/prim_ram_1p_scr.sv +++ b/vendor/lowrisc_ip/ip/prim/rtl/prim_ram_1p_scr.sv @@ -516,4 +516,6 @@ module prim_ram_1p_scr import prim_ram_1p_pkg::*; #( .cfg_i ); + `include "prim_util_get_scramble_params.svh" + endmodule : prim_ram_1p_scr diff --git a/vendor/lowrisc_ip/ip/prim/rtl/prim_rom_pkg.sv b/vendor/lowrisc_ip/ip/prim/rtl/prim_rom_pkg.sv index c81d5781..1ac84ee0 100644 --- a/vendor/lowrisc_ip/ip/prim/rtl/prim_rom_pkg.sv +++ b/vendor/lowrisc_ip/ip/prim/rtl/prim_rom_pkg.sv @@ -10,4 +10,6 @@ package prim_rom_pkg; logic [3:0] cfg; } rom_cfg_t; + parameter rom_cfg_t ROM_CFG_DEFAULT = '0; + endpackage // prim_rom_pkg diff --git a/vendor/lowrisc_ip/ip/prim/rtl/prim_sram_arbiter.sv b/vendor/lowrisc_ip/ip/prim/rtl/prim_sram_arbiter.sv index 2df22ea8..50d2762d 100644 --- a/vendor/lowrisc_ip/ip/prim/rtl/prim_sram_arbiter.sv +++ b/vendor/lowrisc_ip/ip/prim/rtl/prim_sram_arbiter.sv @@ -67,13 +67,14 @@ module prim_sram_arbiter #( ) u_reqarb ( .clk_i, .rst_ni, + .req_chk_i ( 1'b1 ), .req_i, - .data_i ( req_packed ), + .data_i ( req_packed ), .gnt_o, - .idx_o ( ), - .valid_o ( sram_req_o ), - .data_o ( sram_packed ), - .ready_i ( 1'b1 ) + .idx_o ( ), + .valid_o ( sram_req_o ), + .data_o ( sram_packed ), + .ready_i ( 1'b1 ) ); end else if (ArbiterImpl == "BINTREE") begin : gen_tree_arb prim_arbiter_tree #( @@ -82,13 +83,14 @@ module prim_sram_arbiter #( ) u_reqarb ( .clk_i, .rst_ni, + .req_chk_i ( 1'b1 ), .req_i, - .data_i ( req_packed ), + .data_i ( req_packed ), .gnt_o, - .idx_o ( ), - .valid_o ( sram_req_o ), - .data_o ( sram_packed ), - .ready_i ( 1'b1 ) + .idx_o ( ), + .valid_o ( sram_req_o ), + .data_o ( sram_packed ), + .ready_i ( 1'b1 ) ); end else begin : gen_unknown `ASSERT_INIT(UnknownArbImpl_A, 0) diff --git a/vendor/lowrisc_ip/ip/prim/rtl/prim_subreg_shadow.sv b/vendor/lowrisc_ip/ip/prim/rtl/prim_subreg_shadow.sv index 347e57f8..69764191 100644 --- a/vendor/lowrisc_ip/ip/prim/rtl/prim_subreg_shadow.sv +++ b/vendor/lowrisc_ip/ip/prim/rtl/prim_subreg_shadow.sv @@ -33,6 +33,13 @@ module prim_subreg_shadow #( output logic err_storage ); + // Since the shadow and staging registers work with the 1's complement value, + // we need to invert the polarity of the SW access if it is either "W1S" or "W0C". + // W1C is forbidden since the W0S complement is not implemented. + `ASSERT_INIT(CheckSwAccessIsLegal_A, SWACCESS inside {"RW", "RO", "WO", "W1S", "W0C", "RC"}) + parameter bit [3*8-1:0] INVERTED_SWACCESS = (SWACCESS == "W1S") ? "W0C" : + (SWACCESS == "W0C") ? "W1S" : SWACCESS; + // Subreg control signals logic phase_clear; logic phase_q; @@ -86,9 +93,9 @@ module prim_subreg_shadow #( assign staged_we = we & ~phase_q; assign staged_de = de & ~phase_q; prim_subreg #( - .DW ( DW ), - .SWACCESS ( SWACCESS ), - .RESVAL ( ~RESVAL ) + .DW ( DW ), + .SWACCESS ( INVERTED_SWACCESS ), + .RESVAL ( ~RESVAL ) ) staged_reg ( .clk_i ( clk_i ), .rst_ni ( rst_ni ), @@ -109,9 +116,9 @@ module prim_subreg_shadow #( assign shadow_we = we & phase_q & ~err_update; assign shadow_de = de & phase_q & ~err_update; prim_subreg #( - .DW ( DW ), - .SWACCESS ( SWACCESS ), - .RESVAL ( ~RESVAL ) + .DW ( DW ), + .SWACCESS ( INVERTED_SWACCESS ), + .RESVAL ( ~RESVAL ) ) shadow_reg ( .clk_i ( clk_i ), .rst_ni ( rst_ni ), diff --git a/vendor/lowrisc_ip/ip/prim/rtl/prim_sync_reqack.sv b/vendor/lowrisc_ip/ip/prim/rtl/prim_sync_reqack.sv index 45d0c592..b2d036ae 100644 --- a/vendor/lowrisc_ip/ip/prim/rtl/prim_sync_reqack.sv +++ b/vendor/lowrisc_ip/ip/prim/rtl/prim_sync_reqack.sv @@ -23,21 +23,24 @@ `include "prim_assert.sv" -module prim_sync_reqack #( - // Non-functional parameter to switch on the request stability assertion - parameter bit EnReqStabA = 1 -) ( +module prim_sync_reqack ( input clk_src_i, // REQ side, SRC domain input rst_src_ni, // REQ side, SRC domain input clk_dst_i, // ACK side, DST domain input rst_dst_ni, // ACK side, DST domain + input logic req_chk_i, // Used for gating assertions. Drive to 1 during normal operation. + input logic src_req_i, // REQ side, SRC domain output logic src_ack_o, // REQ side, SRC domain output logic dst_req_o, // ACK side, DST domain input logic dst_ack_i // ACK side, DST domain ); + // req_chk_i is used for gating assertions only. + logic unused_req_chk; + assign unused_req_chk = req_chk_i; + // Types typedef enum logic { EVEN, ODD @@ -167,10 +170,9 @@ module prim_sync_reqack #( end end - if (EnReqStabA) begin : gen_lock_assertion - // SRC domain can only de-assert REQ after receiving ACK. - `ASSERT(SyncReqAckHoldReq, $fell(src_req_i) |-> $fell(src_ack_o), clk_src_i, !rst_src_ni) - end + // SRC domain can only de-assert REQ after receiving ACK. + `ASSERT(SyncReqAckHoldReq, $fell(src_req_i) |-> + $fell(src_ack_o), clk_src_i, !rst_src_ni || !req_chk_i) // DST domain cannot assert ACK without REQ. `ASSERT(SyncReqAckAckNeedsReq, dst_ack_i |-> dst_req_o, clk_dst_i, !rst_dst_ni) diff --git a/vendor/lowrisc_ip/ip/prim/rtl/prim_sync_reqack_data.sv b/vendor/lowrisc_ip/ip/prim/rtl/prim_sync_reqack_data.sv index 82e7fa0e..42727e4c 100644 --- a/vendor/lowrisc_ip/ip/prim/rtl/prim_sync_reqack_data.sv +++ b/vendor/lowrisc_ip/ip/prim/rtl/prim_sync_reqack_data.sv @@ -20,15 +20,16 @@ module prim_sync_reqack_data #( parameter int unsigned Width = 1, parameter bit DataSrc2Dst = 1'b1, // Direction of data flow: 1'b1 = SRC to DST, // 1'b0 = DST to SRC - parameter bit DataReg = 1'b0, // Enable optional register stage for data, + parameter bit DataReg = 1'b0 // Enable optional register stage for data, // only usable with DataSrc2Dst == 1'b0. - parameter bit EnReqStabA = 1 // Used in submodule `prim_sync_reqack`. ) ( input clk_src_i, // REQ side, SRC domain input rst_src_ni, // REQ side, SRC domain input clk_dst_i, // ACK side, DST domain input rst_dst_ni, // ACK side, DST domain + input logic req_chk_i, // Used for gating assertions. Drive to 1 during normal operation. + input logic src_req_i, // REQ side, SRC domain output logic src_ack_o, // REQ side, SRC domain output logic dst_req_o, // ACK side, DST domain @@ -41,14 +42,14 @@ module prim_sync_reqack_data #( //////////////////////////////////// // REQ/ACK synchronizer primitive // //////////////////////////////////// - prim_sync_reqack #( - .EnReqStabA(EnReqStabA) - ) u_prim_sync_reqack ( + prim_sync_reqack u_prim_sync_reqack ( .clk_src_i, .rst_src_ni, .clk_dst_i, .rst_dst_ni, + .req_chk_i, + .src_req_i, .src_ack_o, .dst_req_o, diff --git a/vendor/lowrisc_ip/ip/prim/rtl/prim_util_get_scramble_params.svh b/vendor/lowrisc_ip/ip/prim/rtl/prim_util_get_scramble_params.svh new file mode 100644 index 00000000..d9efc28e --- /dev/null +++ b/vendor/lowrisc_ip/ip/prim/rtl/prim_util_get_scramble_params.svh @@ -0,0 +1,36 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +`ifndef SYNTHESIS + export "DPI-C" function simutil_get_scramble_key; + + function int simutil_get_scramble_key(output bit [127:0] val); + if (!key_valid_i) begin + return 0; + end + + if (DataKeyWidth != 128) begin + return 0; + end + + val = key_i; + return 1; + endfunction + + export "DPI-C" function simutil_get_scramble_nonce; + + function int simutil_get_scramble_nonce(output bit [319:0] nonce); + if (!key_valid_i) begin + return 0; + end + + if (NonceWidth > 320) begin + return 0; + end + + nonce = '0; + nonce[NonceWidth-1:0] = nonce_i; + return 1; + endfunction +`endif diff --git a/vendor/lowrisc_ip/ip/prim/util/primgen.py b/vendor/lowrisc_ip/ip/prim/util/primgen.py index 532107c2..eeac8d5d 100755 --- a/vendor/lowrisc_ip/ip/prim/util/primgen.py +++ b/vendor/lowrisc_ip/ip/prim/util/primgen.py @@ -1,15 +1,21 @@ +#!/usr/bin/env python3 # Copyright lowRISC contributors. # Licensed under the Apache License, Version 2.0, see LICENSE for details. # SPDX-License-Identifier: Apache-2.0 import os +import sys + +# Make vendored packages available in the search path. +sys.path.append('vendor') + import re import shutil -import sys import yaml from mako.template import Template + try: from yaml import CSafeLoader as YamlLoader, CSafeDumper as YamlDumper except ImportError: @@ -84,29 +90,76 @@ def _top_module_file(core_files, module_name): return file -def _parse_module_header(generic_impl_filepath, module_name): - """ Parse a SystemVerilog file to extract the 'module' header +def _parse_module_header_verible(generic_impl_filepath, module_name): + """ Parse a SystemVerilog file to extract the 'module' header using Verible - Return a dict with the following entries: - - module_header: the whole module header (including the 'module' keyword) - - package_import_declaration: import declarations - - parameter_port_list: parameter/localparam declarations in the header - - ports: the list of ports. The portlist can be ANSI or non-ANSI style (with - or without signal declarations; see the SV spec for details). + Implementation of _parse_module_header() which uses verible-verilog-syntax + to do the parsing. This is the primary implementation and is used when + supported Verible version is available. + + See _parse_module_header() for API details. """ - # TODO: Evaluate different Python SV parsers and use one instead of this - # beautiful regex. A more complete parser would ignore comments, for - # example, or properly handle matching parentheses ... Or write a minimal - # parser that can parse the module header. - # - # Initial evaluations: - # - https://github.com/PyHDI/Pyverilog: - # requires Icarus and Python 3.6 - # - https://kevinpt.github.io/hdlparse/: - # Didn't do anything in my tests. - # - https://github.com/sepandhaghighi/verilogparser: - # Doesn't support SystemVerilog + from google_verible_verilog_syntax_py.verible_verilog_syntax import VeribleVerilogSyntax, PreOrderTreeIterator + + parser = VeribleVerilogSyntax() + + data = parser.parse_file(generic_impl_filepath, options={"skip_null": True}) + if data.errors: + for err in data.errors: + print(f'Verible: {err.phase} error in line {err.line} column {err.column}' + + (': {err.message}' if err.message else '.')) + # Intentionally not raising an exception here. + # There are chances that Verible recovered from errors. + if not data.tree: + raise ValueError(f"Unable to parse {generic_impl_filepath!r}.") + + module = data.tree.find({"tag": "kModuleDeclaration"}) + header = module.find({"tag": "kModuleHeader"}) + if not header: + raise ValueError("Unable to extract module header from %s." % + (generic_impl_filepath, )) + + name = header.find({"tag": ["SymbolIdentifier", "EscapedIdentifier"]}, + iter_=PreOrderTreeIterator) + if not name: + raise ValueError("Unable to extract module name from %s." % + (generic_impl_filepath, )) + + imports = header.find_all({"tag": "kPackageImportDeclaration"}) + + parameters_list = header.find({"tag": "kFormalParameterList"}) + parameters = set() + if parameters_list: + for parameter in parameters_list.iter_find_all({"tag": + "kParamDeclaration"}): + if parameter.find({"tag": "parameter"}): + parameter_id = parameter.find({"tag": ["SymbolIdentifier", + "EscapedIdentifier"]}) + parameters.add(parameter_id.text) + + ports = header.find({"tag": "kPortDeclarationList"}) + + return { + 'module_header': header.text, + 'package_import_declaration': '\n'.join([i.text for i in imports]), + 'parameter_port_list': + parameters_list.text if parameters_list else '', + 'ports': ports.text if ports else '', + 'parameters': parameters, + 'parser': 'Verible' + } + + +def _parse_module_header_fallback(generic_impl_filepath, module_name): + """ Parse a SystemVerilog file to extract the 'module' header using RegExp + + Legacy implementation of _parse_module_header() using regular expressions. + It is not as robust as Verible-backed implementation, but doesn't need + Verible to work. + + See _parse_module_header() for API details. + """ # Grammar fragments from the SV2017 spec: # @@ -155,7 +208,8 @@ def _parse_module_header(generic_impl_filepath, module_name): 'ports': matches.group('ports').strip() or '', 'parameters': - _parse_parameter_port_list(parameter_port_list) + _parse_parameter_port_list(parameter_port_list), + 'parser': 'Fallback (regex)' } @@ -197,6 +251,26 @@ def _parse_parameter_port_list(parameter_port_list): return parameters +def _parse_module_header(generic_impl_filepath, module_name): + """ Parse a SystemVerilog file to extract the 'module' header + + Return a dict with the following entries: + - module_header: the whole module header (including the 'module' keyword) + - package_import_declaration: import declarations + - parameter_port_list: parameter/localparam declarations in the header + - ports: the list of ports. The portlist can be ANSI or non-ANSI style (with + or without signal declarations; see the SV spec for details). + - parser: parser used to extract the data. + """ + + try: + return _parse_module_header_verible(generic_impl_filepath, module_name) + except Exception as e: + print(e) + print(f"Verible parser failed, using regex fallback instead.") + return _parse_module_header_fallback(generic_impl_filepath, module_name) + + def _check_gapi(gapi): if 'cores' not in gapi: print("Key 'cores' not found in GAPI structure. " @@ -331,11 +405,12 @@ def _generate_abstract_impl(gapi): module_header_imports=generic_hdr['package_import_declaration'], module_header_params=generic_hdr['parameter_port_list'], module_header_ports=generic_hdr['ports'], - num_techlibs= len(techlibs), + num_techlibs=len(techlibs), # Creating the code to instantiate the primitives in the Mako templating # language is tricky to do; do it in Python instead. instances=_create_instances(prim_name, techlibs, - generic_hdr['parameters'])) + generic_hdr['parameters']), + parser_info=generic_hdr['parser']) abstract_prim_sv_filepath = 'prim_%s.sv' % (prim_name) with open(abstract_prim_sv_filepath, 'w') as f: f.write(abstract_prim_sv) diff --git a/vendor/lowrisc_ip/ip/prim/util/primgen/abstract_prim.sv.tpl b/vendor/lowrisc_ip/ip/prim/util/primgen/abstract_prim.sv.tpl index 4a8f92cd..7b28e73f 100644 --- a/vendor/lowrisc_ip/ip/prim/util/primgen/abstract_prim.sv.tpl +++ b/vendor/lowrisc_ip/ip/prim/util/primgen/abstract_prim.sv.tpl @@ -3,6 +3,7 @@ // SPDX-License-Identifier: Apache-2.0 // This file is auto-generated. +// Used parser: ${parser_info} % if num_techlibs > 1: `ifndef PRIM_DEFAULT_IMPL diff --git a/vendor/lowrisc_ip/ip/prim/util/vendor/google_verible_verilog_syntax_py.lock.hjson b/vendor/lowrisc_ip/ip/prim/util/vendor/google_verible_verilog_syntax_py.lock.hjson new file mode 100644 index 00000000..dc471725 --- /dev/null +++ b/vendor/lowrisc_ip/ip/prim/util/vendor/google_verible_verilog_syntax_py.lock.hjson @@ -0,0 +1,15 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +// This file is generated by the util/vendor script. Please do not modify it +// manually. + +{ + upstream: + { + url: https://github.com/google/verible.git + rev: ed1f3a9577047b801854fb36dd14ebe0aecbdddc + only_subdir: verilog/tools/syntax/export_json_examples + } +} diff --git a/vendor/lowrisc_ip/ip/prim/util/vendor/google_verible_verilog_syntax_py.vendor.hjson b/vendor/lowrisc_ip/ip/prim/util/vendor/google_verible_verilog_syntax_py.vendor.hjson new file mode 100644 index 00000000..0aeafcab --- /dev/null +++ b/vendor/lowrisc_ip/ip/prim/util/vendor/google_verible_verilog_syntax_py.vendor.hjson @@ -0,0 +1,13 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +{ + name: "google_verible_verilog_syntax_py", + target_dir: "google_verible_verilog_syntax_py", + + upstream: { + url: "https://github.com/google/verible.git", + rev: "v0.0-943-ged1f3a9", + only_subdir: "verilog/tools/syntax/export_json_examples", + }, +} diff --git a/vendor/lowrisc_ip/ip/prim/util/vendor/google_verible_verilog_syntax_py/BUILD b/vendor/lowrisc_ip/ip/prim/util/vendor/google_verible_verilog_syntax_py/BUILD new file mode 100644 index 00000000..85da8878 --- /dev/null +++ b/vendor/lowrisc_ip/ip/prim/util/vendor/google_verible_verilog_syntax_py/BUILD @@ -0,0 +1,55 @@ +load("@rules_python//python:defs.bzl", "py_test", "py_library", "py_binary") + +licenses(["notice"]) + +py_library( + name = "verible_verilog_syntax_py", + srcs = ["verible_verilog_syntax.py"], + srcs_version = "PY3", + imports = ["."], + data = ["//verilog/tools/syntax:verible-verilog-syntax"], + deps = [ + "@python_anytree//:anytree", + "//third_party/py/dataclasses", + ], +) + +py_test( + name = "verible_verilog_syntax_py_test", + size = "small", + srcs = ["verible_verilog_syntax_test.py"], + srcs_version = "PY3", + python_version = "PY3", + main = "verible_verilog_syntax_test.py", + deps = [":verible_verilog_syntax_py"], + data = ["//verilog/tools/syntax:verible-verilog-syntax"], + args = ["$(location //verilog/tools/syntax:verible-verilog-syntax)"], +) + +py_binary( + name = "print_modules", + srcs = ["print_modules.py"], + srcs_version = "PY3", + python_version = "PY3", + main = "print_modules.py", + deps = [ + ":verible_verilog_syntax_py", + "@python_anytree//:anytree", + ], + data = ["//verilog/tools/syntax:verible-verilog-syntax"], + args = ["$(location //verilog/tools/syntax:verible-verilog-syntax)"], +) + +py_binary( + name = "print_tree", + srcs = ["print_tree.py"], + srcs_version = "PY3", + python_version = "PY3", + main = "print_tree.py", + deps = [ + ":verible_verilog_syntax_py", + "@python_anytree//:anytree", + ], + data = ["//verilog/tools/syntax:verible-verilog-syntax"], + args = ["$(location //verilog/tools/syntax:verible-verilog-syntax)"], +) diff --git a/vendor/lowrisc_ip/ip/prim/util/vendor/google_verible_verilog_syntax_py/print_modules.py b/vendor/lowrisc_ip/ip/prim/util/vendor/google_verible_verilog_syntax_py/print_modules.py new file mode 100755 index 00000000..2974e66e --- /dev/null +++ b/vendor/lowrisc_ip/ip/prim/util/vendor/google_verible_verilog_syntax_py/print_modules.py @@ -0,0 +1,134 @@ +#!/usr/bin/env python3 +# Copyright 2017-2020 The Verible Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Print module name, ports, parameters and imports. + +Usage: print_modules.py PATH_TO_VERIBLE_VERILOG_SYNTAX \\ + VERILOG_FILE [VERILOG_FILE [...]] + +This example shows how to use ``verible-verilog-syntax --export_json ...`` +output to extract information about module declarations found in System Verilog +source files. Extracted information: + +* module name +* module port names +* module parameter names +* module imports +* module header code +""" + +import sys + +import anytree + +import verible_verilog_syntax + + +def process_file_data(path: str, data: verible_verilog_syntax.SyntaxData): + """Print information about modules found in SystemVerilog file. + + This function uses verible_verilog_syntax.Node methods to find module + declarations and specific tokens containing following information: + + * module name + * module port names + * module parameter names + * module imports + * module header code + + Args: + path: Path to source file (used only for informational purposes) + data: Parsing results returned by one of VeribleVerilogSyntax' parse_* + methods. + """ + if not data.tree: + return + + modules_info = [] + + # Collect information about each module declaration in the file + for module in data.tree.iter_find_all({"tag": "kModuleDeclaration"}): + module_info = { + "header_text": "", + "name": "", + "ports": [], + "parameters": [], + "imports": [], + } + + # Find module header + header = module.find({"tag": "kModuleHeader"}) + if not header: + continue + module_info["header_text"] = header.text + + # Find module name + name = header.find({"tag": ["SymbolIdentifier", "EscapedIdentifier"]}, + iter_=anytree.PreOrderIter) + if not name: + continue + module_info["name"] = name.text + + # Get the list of ports + for port in header.iter_find_all({"tag": ["kPortDeclaration", "kPort"]}): + port_id = port.find({"tag": ["SymbolIdentifier", "EscapedIdentifier"]}) + module_info["ports"].append(port_id.text) + + # Get the list of parameters + for param in header.iter_find_all({"tag": ["kParamDeclaration"]}): + param_id = param.find({"tag": ["SymbolIdentifier", "EscapedIdentifier"]}) + module_info["parameters"].append(param_id.text) + + # Get the list of imports + for pkg in module.iter_find_all({"tag": ["kPackageImportItem"]}): + module_info["imports"].append(pkg.text) + + modules_info.append(module_info) + + # Print results + if len(modules_info) > 0: + print(f"\033[1;97;7m{path} \033[0m\n") + + def print_entry(key, values): + fmt_values = [f"\033[92m{v}\033[0m" for v in values] + value_part = (f"\n\033[33m// {' '*len(key)}".join(fmt_values) + or "\033[90m-\033[0m") + print(f"\033[33m// \033[93m{key}{value_part}") + + for module_info in modules_info: + print_entry("name: ", [module_info["name"]]) + print_entry("ports: ", module_info["ports"]) + print_entry("parameters: ", module_info["parameters"]) + print_entry("imports: ", module_info["imports"]) + print(f"\033[97m{module_info['header_text']}\033[0m\n") + + +def main(): + if len(sys.argv) < 3: + print(f"Usage: {sys.argv[0]} PATH_TO_VERIBLE_VERILOG_SYNTAX " + + "VERILOG_FILE [VERILOG_FILE [...]]") + return 1 + + parser_path = sys.argv[1] + files = sys.argv[2:] + + parser = verible_verilog_syntax.VeribleVerilogSyntax(executable=parser_path) + data = parser.parse_files(files) + + for file_path, file_data in data.items(): + process_file_data(file_path, file_data) + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/vendor/lowrisc_ip/ip/prim/util/vendor/google_verible_verilog_syntax_py/print_tree.py b/vendor/lowrisc_ip/ip/prim/util/vendor/google_verible_verilog_syntax_py/print_tree.py new file mode 100755 index 00000000..057c6da8 --- /dev/null +++ b/vendor/lowrisc_ip/ip/prim/util/vendor/google_verible_verilog_syntax_py/print_tree.py @@ -0,0 +1,69 @@ +#!/usr/bin/env python3 +# Copyright 2017-2020 The Verible Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Pretty-print Verilog Concrete Syntax Tree + +Usage: print_tree.py PATH_TO_VERIBLE_VERILOG_SYNTAX \\ + VERILOG_FILE [VERILOG_FILE [...]] + +Visualizes tree generated by ``verible-verilog-syntax --export_json ...``. +Values enclosed in ``[]`` are node tags. ``@(S-E)`` marks token's start (S) +and end (E) byte offsets in source code. When a token's text in source code +is not the same as its tag, the text is printed in single quotes. +""" + +import sys + +import anytree + +import verible_verilog_syntax + + +def process_file_data(path: str, data: verible_verilog_syntax.SyntaxData): + """Print tree representation to the console. + + The function uses anytree module (which is a base of a tree implementation + used in verible_verilog_syntax) method to print syntax tree representation + to the console. + + Args: + path: Path to source file (used only for informational purposes) + data: Parsing results returned by one of VeribleVerilogSyntax' parse_* + methods. + """ + print(f"\033[1;97;7m{path} \033[0m\n") + if data.tree: + for prefix, _, node in anytree.RenderTree(data.tree): + print(f"\033[90m{prefix}\033[0m{node.to_formatted_string()}") + print() + + +def main(): + if len(sys.argv) < 3: + print(f"Usage: {sys.argv[0]} PATH_TO_VERIBLE_VERILOG_SYNTAX " + + "VERILOG_FILE [VERILOG_FILE [...]]") + return 1 + + parser_path = sys.argv[1] + files = sys.argv[2:] + + parser = verible_verilog_syntax.VeribleVerilogSyntax(executable=parser_path) + data = parser.parse_files(files, options={"gen_tree": True}) + + for file_path, file_data in data.items(): + process_file_data(file_path, file_data) + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/vendor/lowrisc_ip/ip/prim/util/vendor/google_verible_verilog_syntax_py/verible_verilog_syntax.py b/vendor/lowrisc_ip/ip/prim/util/vendor/google_verible_verilog_syntax_py/verible_verilog_syntax.py new file mode 100644 index 00000000..f7cb4f09 --- /dev/null +++ b/vendor/lowrisc_ip/ip/prim/util/vendor/google_verible_verilog_syntax_py/verible_verilog_syntax.py @@ -0,0 +1,531 @@ +# Copyright 2017-2020 The Verible Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Wrapper for ``verible-verilog-syntax --export_json``""" + +import collections +import json +import re +import subprocess +from typing import Any, Callable, Dict, Iterable, List, Optional, Union + +import anytree +import dataclasses + +_CSI_SEQUENCE = re.compile("\033\\[.*?m") + + +def _colorize(formats: List[str], strings: List[str]) -> str: + result = "" + fi = 0 + for s in strings: + result += f"\033[{formats[fi]}m{s}\033[0m" + fi = (fi+1) % len(formats) + return result + + +# Type aliases + +CallableFilter = Callable[["Node"], bool] +KeyValueFilter = Dict[str, Union[str, List[str]]] +TreeIterator = Union["_TreeIteratorBase", anytree.iterators.AbstractIter] + + +# Custom tree iterators with an option for reverse children iteration + +class _TreeIteratorBase: + def __init__(self, tree: "Node", + filter_: Optional[CallableFilter] = None, + reverse_children: bool = False): + self.tree = tree + self.reverse_children = reverse_children + self.filter_ = filter_ if filter_ else lambda n: True + + def __iter__(self) -> Iterable["Node"]: + yield from self._iter_tree(self.tree) + + def _iter_children(self, tree: Optional["Node"]) -> Iterable["Node"]: + if not tree or not hasattr(tree, "children"): + return [] + return tree.children if not self.reverse_children \ + else reversed(tree.children) + + def _iter_tree(self, tree: Optional["Node"]) -> Iterable["Node"]: + raise NotImplementedError("Subclass must implement '_iter_tree' method") + + +class PreOrderTreeIterator(_TreeIteratorBase): + def _iter_tree(self, tree: Optional["Node"]) -> Iterable["Node"]: + if self.filter_(tree): + yield tree + for child in self._iter_children(tree): + yield from self._iter_tree(child) + + +class PostOrderTreeIterator(_TreeIteratorBase): + def _iter_tree(self, tree: Optional["Node"]) -> Iterable["Node"]: + for child in self._iter_children(tree): + yield from self._iter_tree(child) + if self.filter_(tree): + yield tree + + +class LevelOrderTreeIterator(_TreeIteratorBase): + def _iter_tree(self, tree: Optional["Node"]) -> Iterable["Node"]: + queue = collections.deque([tree]) + while len(queue) > 0: + n = queue.popleft() + if self.filter_(n): + yield n + queue.extend(self._iter_children(n)) + + +class Node(anytree.NodeMixin): + """Base VeribleVerilogSyntax syntax tree node. + + Attributes: + parent (Optional[Node]): Parent node. + """ + def __init__(self, parent: Optional["Node"] = None): + self.parent = parent + + @property + def syntax_data(self) -> Optional["SyntaxData"]: + """Parent SyntaxData""" + return self.parent.syntax_data if self.parent else None + + @property + def start(self) -> Optional[int]: + """Byte offset of node's first character in source text""" + raise NotImplementedError("Subclass must implement 'start' property") + + @property + def end(self) -> Optional[int]: + """Byte offset of a character just past the node in source text.""" + raise NotImplementedError("Subclass must implement 'end' property") + + @property + def text(self) -> str: + """Source code fragment spanning all tokens in a node.""" + start = self.start + end = self.end + sd = self.syntax_data + if ((start is not None) and (end is not None) and sd and sd.source_code + and end <= len(sd.source_code)): + return sd.source_code[start:end].decode("utf-8") + return "" + + def __repr__(self) -> str: + return _CSI_SEQUENCE.sub("", self.to_formatted_string()) + + def to_formatted_string(self) -> str: + """Print node representation formatted for printing in terminal.""" + return super().__repr__() + + +class BranchNode(Node): + """Syntax tree branch node + + Attributes: + tag (str): Node tag. + children (Optional[Node]): Child nodes. + """ + def __init__(self, tag: str, parent: Optional[Node] = None, + children: Optional[List[Node]] = None): + super().__init__(parent) + self.tag = tag + self.children = children if children is not None else [] + + @property + def start(self) -> Optional[int]: + first_token = self.find(lambda n: isinstance(n, TokenNode), + iter_=PostOrderTreeIterator) + return first_token.start if first_token else None + + @property + def end(self) -> Optional[int]: + last_token = self.find(lambda n: isinstance(n, TokenNode), + iter_=PostOrderTreeIterator, reverse_children=True) + return last_token.end if last_token else None + + def iter_find_all(self, filter_: Union[CallableFilter, KeyValueFilter, None], + max_count: int = 0, + iter_: TreeIterator = LevelOrderTreeIterator, + **kwargs) -> Iterable[Node]: + """Iterate all nodes matching specified filter. + + Args: + filter_: Describes what to search for. Might be: + * Callable taking Node as an argument and returning True for accepted + nodes. + * Dict mapping Node attribute names to searched value or list of + searched values. + max_count: Stop searching after finding that many matching nodes. + iter_: Tree iterator. Decides in what order nodes are visited. + + Yields: + Nodes matching specified filter. + """ + def as_list(v): + return v if isinstance(v, list) else [v] + + if filter_ and not callable(filter_): + filters = filter_ + def f(node): + for attr,value in filters.items(): + if not hasattr(node, attr): + return False + if getattr(node, attr) not in as_list(value): + return False + return True + filter_ = f + + for node in iter_(self, filter_, **kwargs): + yield node + max_count -= 1 + if max_count == 0: + break + + def find(self, filter_: Union[CallableFilter, KeyValueFilter, None], + iter_: TreeIterator = LevelOrderTreeIterator, **kwargs) \ + -> Optional[Node]: + """Find node matching specified filter. + + Args: + filter_: Describes what to search for. Might be: + * Callable taking Node as an argument and returning True for accepted + node. + * Dict mapping Node attribute names to searched value or list of + searched values. + iter_: Tree iterator. Decides in what order nodes are visited. + + Returns: + First Node matching filter. + """ + return next(self.iter_find_all(filter_, max_count=1, iter_=iter_, + **kwargs), None) + + def find_all(self, filter_: Union[CallableFilter, KeyValueFilter, None], + max_count: int = 0, iter_: TreeIterator = LevelOrderTreeIterator, + **kwargs) -> List[Node]: + """Find all nodes matching specified filter. + + Args: + filter_: Describes what to search for. Might be: + * Callable taking Node as an argument and returning True for accepted + nodes. + * Dict mapping Node attribute names to searched value or list of + searched values. + max_count: Stop searching after finding that many matching nodes. + iter_: Tree iterator. Decides in what order nodes are visited. + + Returns: + List of nodes matching specified filter. + """ + return list(self.iter_find_all(filter_, max_count=max_count, iter_=iter_, + **kwargs)) + + def to_formatted_string(self) -> str: + tag = self.tag if self.tag == repr(self.tag)[1:-1] else repr(self.tag) + return _colorize(["37", "1;97"], ["[", tag, "]"]) + + +class RootNode(BranchNode): + """Syntax tree root node.""" + def __init__(self, tag: str, syntax_data: Optional["SyntaxData"] = None, + children: Optional[List[Node]] = None): + super().__init__(tag, None, children) + self._syntax_data = syntax_data + + @property + def syntax_data(self) -> Optional["SyntaxData"]: + return self._syntax_data + + +class LeafNode(Node): + """Syntax tree leaf node. + + This specific class is used for null nodes. + """ + @property + def start(self) -> None: + """Byte offset of token's first character in source text""" + return None + + @property + def end(self) -> None: + """Byte offset of a character just past the token in source text.""" + return None + + def to_formatted_string(self) -> str: + return _colorize(["90"], ["null"]) + + +class TokenNode(LeafNode): + """Tree node with token data + + Represents single token in a syntax tree. + + Attributes: + tag (str): Token tag. + """ + + def __init__(self, tag: str, start: int, end: int, + parent: Optional[Node] = None): + super().__init__(parent) + self.tag = tag + self._start = start + self._end = end + + @property + def start(self) -> int: + return self._start + + @property + def end(self) -> int: + return self._end + + def to_formatted_string(self) -> str: + tag = self.tag if self.tag == repr(self.tag)[1:-1] else repr(self.tag) + parts = [ + _colorize(["37", "1;97"], ["[", tag, "]"]), + _colorize(["33", "93"], ["@(", self.start, "-", self.end, ")"]), + ] + text = self.text + if self.tag != text: + parts.append(_colorize(["32", "92"], ["'", repr(text)[1:-1], "'"])) + return " ".join(parts) + + +class Token: + """Token data + + Represents single token in tokens and rawtokens lists. + + Attributes: + tag (str): Token tag. + start (int): Byte offset of token's first character in source text. + end (int): Byte offset of a character just past the token in source text. + syntax_data (Optional["SyntaxData"]): Parent SyntaxData. + """ + + def __init__(self, tag: str, start: int, end: int, + syntax_data: Optional["SyntaxData"] = None): + self.tag = tag + self.start = start + self.end = end + self.syntax_data = syntax_data + + @property + def text(self) -> str: + """Token text in source code.""" + sd = self.syntax_data + if sd and sd.source_code and self.end <= len(sd.source_code): + return sd.source_code[self.start:self.end].decode("utf-8") + return "" + + def __repr__(self) -> str: + return _CSI_SEQUENCE.sub("", self.to_formatted_string()) + + def to_formatted_string(self) -> str: + tag = self.tag if self.tag == repr(self.tag)[1:-1] else repr(self.tag) + parts = [ + _colorize(["37", "1;97"], ["[", tag, "]"]), + _colorize(["33", "93"], ["@(", self.start, "-", self.end, ")"]), + _colorize(["32", "92"], ["'", repr(self.text)[1:-1], "'"]), + ] + return " ".join(parts) + + +@dataclasses.dataclass +class Error: + line: int + column: int + phase: str + message: str = "" + + +@dataclasses.dataclass +class SyntaxData: + source_code: Optional[str] = None + tree: Optional[RootNode] = None + tokens: Optional[List[Token]] = None + rawtokens: Optional[List[Token]] = None + errors: Optional[List[Error]] = None + + +class VeribleVerilogSyntax: + """``verible-verilog-syntax`` wrapper. + + This class provides methods for running ``verible-verilog-syntax`` and + transforming its output into Python data structures. + + Args: + executable: path to ``verible-verilog-syntax`` binary. + """ + + def __init__(self, executable: str = "verible-verilog-syntax"): + self.executable = executable + + @staticmethod + def _transform_tree(tree, data: SyntaxData, skip_null: bool) -> RootNode: + def transform(tree): + if tree is None: + return None + if "children" in tree: + children = [ + transform(child) or LeafNode() + for child in tree["children"] + if not (skip_null and child is None) + ] + tag = tree["tag"] + return BranchNode(tag, children=children) + tag = tree["tag"] + start = tree["start"] + end = tree["end"] + return TokenNode(tag, start, end) + + if "children" not in tree: + return None + + children = [ + transform(child) or LeafNode() + for child in tree["children"] + if not (skip_null and child is None) + ] + tag = tree["tag"] + return RootNode(tag, syntax_data=data, children=children) + + + @staticmethod + def _transform_tokens(tokens, data: SyntaxData) -> List[Token]: + return [Token(t["tag"], t["start"], t["end"], data) for t in tokens] + + + @staticmethod + def _transform_errors(tokens) -> List[Error]: + return [Error(t["line"], t["column"], t["phase"], t.get("message", None)) + for t in tokens] + + def _parse(self, paths: List[str], input_: str = None, + options: Dict[str, Any] = None) -> Dict[str, SyntaxData]: + """Common implementation of parse_* methods""" + options = { + "gen_tree": True, + "skip_null": False, + "gen_tokens": False, + "gen_rawtokens": False, + **(options or {}), + } + + args = ["-export_json"] + if options["gen_tree"]: + args.append("-printtree") + if options["gen_tokens"]: + args.append("-printtokens") + if options["gen_rawtokens"]: + args.append("-printrawtokens") + + proc = subprocess.run([self.executable, *args , *paths], + stdout=subprocess.PIPE, + input=input_, + encoding="utf-8", + check=False) + + json_data = json.loads(proc.stdout) + data = {} + for file_path, file_json in json_data.items(): + file_data = SyntaxData() + + if file_path == "-": + file_data.source_code = input_.encode("utf-8") + else: + with open(file_path, "rb") as f: + file_data.source_code = f.read() + + if "tree" in file_json: + file_data.tree = VeribleVerilogSyntax._transform_tree( + file_json["tree"], file_data, options["skip_null"]) + + if "tokens" in file_json: + file_data.tokens = VeribleVerilogSyntax._transform_tokens( + file_json["tokens"], file_data) + + if "rawtokens" in file_json: + file_data.rawtokens = VeribleVerilogSyntax._transform_tokens( + file_json["rawtokens"], file_data) + + if "errors" in file_json: + file_data.errors = VeribleVerilogSyntax._transform_errors( + file_json["errors"]) + + data[file_path] = file_data + + return data + + def parse_files(self, paths: List[str], options: Dict[str, Any] = None) \ + -> Dict[str, SyntaxData]: + """Parse multiple SystemVerilog files. + + Args: + paths: list of paths to files to parse. + options: dict with parsing options. + Available options: + gen_tree (boolean): whether to generate syntax tree. + skip_null (boolean): null nodes won't be stored in a tree if True. + gen_tokens (boolean): whether to generate tokens list. + gen_rawtokens (boolean): whether to generate raw token list. + By default only ``gen_tree`` is True. + + Returns: + A dict that maps file names to their parsing results in SyntaxData object. + """ + return self._parse(paths, options = options) + + def parse_file(self, path: str, options: Dict[str, Any] = None) \ + -> Optional[SyntaxData]: + """Parse single SystemVerilog file. + + Args: + path: path to a file to parse. + options: dict with parsing options. + Available options: + gen_tree (boolean): whether to generate syntax tree. + skip_null (boolean): null nodes won't be stored in a tree if True. + gen_tokens (boolean): whether to generate tokens list. + gen_rawtokens (boolean): whether to generate raw token list. + By default only ``gen_tree`` is True. + + Returns: + Parsing results in SyntaxData object. + """ + return self._parse([path], options = options).get(path, None) + + def parse_string(self, string: str, options: Dict[str, Any] = None) \ + -> Optional[SyntaxData]: + """Parse a string with SystemVerilog code. + + Args: + string: SystemVerilog code to parse. + options: dict with parsing options. + Available options: + gen_tree (boolean): whether to generate syntax tree. + skip_null (boolean): null nodes won't be stored in a tree if True. + gen_tokens (boolean): whether to generate tokens list. + gen_rawtokens (boolean): whether to generate raw token list. + By default only ``gen_tree`` is True. + + Returns: + Parsing results in SyntaxData object. + """ + return self._parse(["-"], input_=string, options=options).get("-", None) diff --git a/vendor/lowrisc_ip/ip/prim/util/vendor/google_verible_verilog_syntax_py/verible_verilog_syntax_test.py b/vendor/lowrisc_ip/ip/prim/util/vendor/google_verible_verilog_syntax_py/verible_verilog_syntax_test.py new file mode 100755 index 00000000..c183bee7 --- /dev/null +++ b/vendor/lowrisc_ip/ip/prim/util/vendor/google_verible_verilog_syntax_py/verible_verilog_syntax_test.py @@ -0,0 +1,272 @@ +#!/usr/bin/env python3 +# Copyright 2017-2020 The Verible Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""VeribleVerilogSyntax test""" + + +import sys +import tempfile +import unittest + +import verible_verilog_syntax + + +class VeribleVerilogSyntaxTest(unittest.TestCase): + def setUp(self): + if len(sys.argv) > 1: + self.parser = verible_verilog_syntax.VeribleVerilogSyntax( + executable=sys.argv[1]) + else: + self.parser = verible_verilog_syntax.VeribleVerilogSyntax() + + self.assertIsNotNone(self.parser) + + def create_temp_file(self, content): + tmp = tempfile.NamedTemporaryFile(mode="w", suffix=".sv") + tmp.write(content) + tmp.flush() + return tmp + + +class TestParseMethods(VeribleVerilogSyntaxTest): + def test_parse_string(self): + data = self.parser.parse_string("module X(); endmodule;") + self.assertIsNotNone(data) + self.assertIsNotNone(data.tree) + self.assertIsNone(data.errors) + + def test_parse_string_with_all_options(self): + data = self.parser.parse_string("module X(); endmodule;", options={ + "gen_tree": True, + "gen_tokens": True, + "gen_rawtokens": True, + }) + self.assertIsNotNone(data) + self.assertIsNotNone(data.tree) + self.assertIsNotNone(data.tokens) + self.assertIsNotNone(data.rawtokens) + self.assertIsNone(data.errors) + + def test_parse_string_error(self): + data = self.parser.parse_string("endmodule X(); module;") + self.assertIsNotNone(data) + self.assertIsNotNone(data.errors) + + def test_parse_file(self): + file_ = self.create_temp_file("module X(); endmodule;") + data = self.parser.parse_file(file_.name) + self.assertIsNotNone(data) + self.assertIsNotNone(data.tree) + self.assertIsNone(data.errors) + + def test_parse_file_with_all_options(self): + file_ = self.create_temp_file("module X(); endmodule;") + data = self.parser.parse_file(file_.name, options={ + "gen_tree": True, + "gen_tokens": True, + "gen_rawtokens": True, + }) + self.assertIsNotNone(data) + self.assertIsNotNone(data.tree) + self.assertIsNotNone(data.tokens) + self.assertIsNotNone(data.rawtokens) + self.assertIsNone(data.errors) + + def test_parse_file_error(self): + file_ = self.create_temp_file("endmodule X(); module;") + data = self.parser.parse_file(file_.name) + self.assertIsNotNone(data) + self.assertIsNotNone(data.errors) + + def test_parse_files(self): + files = [ + self.create_temp_file("module X(); endmodule;"), + self.create_temp_file("module Y(); endmodule;"), + ] + data = self.parser.parse_files([f.name for f in files]) + self.assertIsNotNone(data) + for f in files: + self.assertIsNotNone(data[f.name]) + self.assertIsNotNone(data[f.name].tree) + self.assertIsNone(data[f.name].errors) + + def test_parse_files_with_all_options(self): + files = [ + self.create_temp_file("module X(); endmodule;"), + self.create_temp_file("module Y(); endmodule;"), + ] + data = self.parser.parse_files([f.name for f in files], options={ + "gen_tree": True, + "gen_tokens": True, + "gen_rawtokens": True, + }) + self.assertIsNotNone(data) + for f in files: + self.assertIsNotNone(data[f.name]) + self.assertIsNotNone(data[f.name].tree) + self.assertIsNotNone(data[f.name].tokens) + self.assertIsNotNone(data[f.name].rawtokens) + self.assertIsNone(data[f.name].errors) + + def test_parse_files_error(self): + # One file with, and one without errors + files = [ + self.create_temp_file("endmodule X(); module;"), + self.create_temp_file("module Y(); endmodule;"), + ] + data = self.parser.parse_files([f.name for f in files]) + self.assertIsNotNone(data) + for f in files: + self.assertIsNotNone(data[f.name]) + + self.assertIsNotNone(data[files[0].name].errors) + self.assertIsNone(data[files[1].name].errors) + + +class TestTree(VeribleVerilogSyntaxTest): + def setUp(self): + super().setUp() + data = self.parser.parse_string( + "module ModuleName#(parameter PARAM_NAME=42)\n" + + " (input portIn, output portOut);\n" + + " import import_pkg_name::*;\n" + + " wire wireName;\n" + + "endmodule;\n" + + "module OtherModule; endmodule;\n") + self.assertIsNotNone(data) + self.assertIsNotNone(data.tree) + self.tree = data.tree + + def test_find(self): + header = self.tree.find({"tag": "kModuleHeader"}) + self.assertIsNotNone(header) + module_name = header.find({"tag": "SymbolIdentifier"}) + self.assertIsNotNone(module_name) + self.assertEqual(module_name.text, "ModuleName") + nonexistent = header.find({"tag": "SomeUndefinedTag"}) + self.assertIsNone(nonexistent) + + def test_find_all(self): + headers = self.tree.find_all({"tag": "kModuleHeader"}) + self.assertEqual(len(headers), 2) + + identifiers = self.tree.find_all({"tag": "SymbolIdentifier"}) + self.assertEqual(len(identifiers), 7) + + some_identifiers = self.tree.find_all({"tag": "SymbolIdentifier"}, + max_count=4) + self.assertEqual(len(some_identifiers), 4) + + def test_iter_find_all(self): + identifiers = [n.text + for n + in self.tree.iter_find_all({"tag": "SymbolIdentifier"})] + self.assertIn("ModuleName", identifiers) + self.assertIn("PARAM_NAME", identifiers) + self.assertIn("OtherModule", identifiers) + + def test_custom_filter(self): + def tokens_past_135_byte(node): + return (isinstance(node, verible_verilog_syntax.TokenNode) + and node.start > 135) + + other_module_tokens = self.tree.find_all(tokens_past_135_byte) + self.assertGreaterEqual(len(other_module_tokens), 5) + for token in other_module_tokens: + self.assertGreater(token.start, 135) + + def test_search_order(self): + level_order = self.tree.find_all({"tag": "SymbolIdentifier"}) + depth_order = self.tree.find_all({"tag": "SymbolIdentifier"}, + iter_=verible_verilog_syntax.PreOrderTreeIterator) + + def check_items_order(iterable, items_to_check): + index = 0 + for item in iterable: + if items_to_check[index] == item: + index += 1 + if index == len(items_to_check): + return True + return False + + self.assertTrue(check_items_order([n.text for n in level_order], + ["ModuleName", "OtherModule", "portIn"])) + self.assertTrue(check_items_order([n.text for n in depth_order], + ["ModuleName", "portIn", "OtherModule"])) + + def test_node_properties(self): + header = self.tree.find({"tag": "kModuleHeader"}) + self.assertIsNotNone(header) + + module_kw = header.find({"tag": "module"}) + self.assertEqual(module_kw.text, "module") + self.assertEqual(module_kw.start, 0) + self.assertEqual(module_kw.end, 6) + + semicolon = header.find({"tag": ";"}) + self.assertEqual(semicolon.text, ";") + self.assertEqual(semicolon.start, 78) + self.assertEqual(semicolon.end, 79) + + self.assertEqual(header.start, module_kw.start) + self.assertEqual(header.end, semicolon.end) + self.assertTrue(header.text.startswith("module")) + self.assertTrue(header.text.endswith(");")) + + +class TestTokens(VeribleVerilogSyntaxTest): + def test_tokens(self): + data = self.parser.parse_string( + "module X(input portIn, output portOut); endmodule;", options={ + "gen_tree": False, + "gen_tokens": True, + }) + + self.assertIsNotNone(data) + self.assertIsNotNone(data.tokens) + + identifiers = [t for t in data.tokens if t.tag == "SymbolIdentifier"] + + module_name = identifiers[0] + self.assertEqual(module_name.text, "X") + self.assertEqual(module_name.start, 7) + self.assertEqual(module_name.end, 8) + + texts = [t.text for t in identifiers] + self.assertSequenceEqual(texts, ["X", "portIn", "portOut"]) + + + def test_rawtokens(self): + data = self.parser.parse_string( + "module X(input portIn, output portOut); endmodule;", options={ + "gen_tree": False, + "gen_rawtokens": True, + }) + + self.assertIsNotNone(data) + self.assertIsNotNone(data.rawtokens) + + identifiers = [t for t in data.rawtokens if t.tag == "SymbolIdentifier"] + + module_name = identifiers[0] + self.assertEqual(module_name.text, "X") + self.assertEqual(module_name.start, 7) + self.assertEqual(module_name.end, 8) + + texts = [t.text for t in identifiers] + self.assertSequenceEqual(texts, ["X", "portIn", "portOut"]) + + +if __name__ == "__main__": + unittest.main(argv=sys.argv[0:1], verbosity=2) diff --git a/vendor/lowrisc_ip/ip/prim_generic/lint/prim_generic_otp.waiver b/vendor/lowrisc_ip/ip/prim_generic/lint/prim_generic_otp.waiver index 341732af..cba6e283 100644 --- a/vendor/lowrisc_ip/ip/prim_generic/lint/prim_generic_otp.waiver +++ b/vendor/lowrisc_ip/ip/prim_generic/lint/prim_generic_otp.waiver @@ -6,3 +6,5 @@ waive -rules {CONST_FF} -location {prim_generic_otp.sv} -msg {Flip-flop 'err_q[3]' is driven by constant zero} \ -comment "Due to the error encoding, this bit is always constant in this module." +waive -rules {INOUT_AS_IN} -location {prim_generic_otp.sv} -msg {Inout port 'ext_voltage_io' has no driver in module 'prim_generic_otp'} \ + -comment "This signal is not driven in the generic model." diff --git a/vendor/lowrisc_ip/ip/prim_generic/rtl/prim_generic_otp.sv b/vendor/lowrisc_ip/ip/prim_generic/rtl/prim_generic_otp.sv index e941b3a1..5e4a8cae 100644 --- a/vendor/lowrisc_ip/ip/prim_generic/rtl/prim_generic_otp.sv +++ b/vendor/lowrisc_ip/ip/prim_generic/rtl/prim_generic_otp.sv @@ -6,29 +6,40 @@ module prim_generic_otp import prim_otp_pkg::*; #( // Native OTP word size. This determines the size_i granule. - parameter int Width = 16, - parameter int Depth = 1024, + parameter int Width = 16, + parameter int Depth = 1024, // This determines the maximum number of native words that // can be transferred accross the interface in one cycle. - parameter int SizeWidth = 2, + parameter int SizeWidth = 2, // Width of the power sequencing signal. - parameter int PwrSeqWidth = 2, + parameter int PwrSeqWidth = 2, // Number of Test TL-UL words - parameter int TlDepth = 16, + parameter int TlDepth = 16, + // Width of vendor-specific test control signal + parameter int TestCtrlWidth = 8, // Derived parameters - localparam int AddrWidth = prim_util_pkg::vbits(Depth), - localparam int IfWidth = 2**SizeWidth*Width, + localparam int AddrWidth = prim_util_pkg::vbits(Depth), + localparam int IfWidth = 2**SizeWidth*Width, // VMEM file to initialize the memory with - parameter MemInitFile = "" + parameter MemInitFile = "" ) ( input clk_i, input rst_ni, // Macro-specific power sequencing signals to/from AST output logic [PwrSeqWidth-1:0] pwr_seq_o, input [PwrSeqWidth-1:0] pwr_seq_h_i, + // External programming voltage + inout wire ext_voltage_io, // Test interface + input [TestCtrlWidth-1:0] test_ctrl_i, input tlul_pkg::tl_h2d_t test_tl_i, output tlul_pkg::tl_d2h_t test_tl_o, + // Other DFT signals + input lc_ctrl_pkg::lc_tx_t scanmode_i, // Scan Mode input + input scan_en_i, // Scan Shift + input scan_rst_ni, // Scan Reset + // Alert indication + output ast_pkg::ast_dif_t otp_alert_src_o, // Ready valid handshake for read/write command output logic ready_o, input valid_i, @@ -39,20 +50,14 @@ module prim_generic_otp // Response channel output logic valid_o, output logic [IfWidth-1:0] rdata_o, - output err_e err_o, - // External programming voltage - inout wire ext_voltage_io, //TODO enable it after the change in prim_otp file - input ext_voltage_en_i, // TODO - //// alert indication - ////////////////////////// - output ast_pkg::ast_dif_t otp_alert_src_o, - - // Scan - input lc_ctrl_pkg::lc_tx_t scanmode_i, // Scan Mode input - input scan_en_i, // Scan Shift - input scan_rst_ni // Scan Reset + output err_e err_o ); + // This is only restricted by the supported ECC poly further + // below, and is straightforward to extend, if needed. + localparam int EccWidth = 6; + `ASSERT_INIT(SecDecWidth_A, Width == 16) + // Not supported in open-source emulation model. logic [PwrSeqWidth-1:0] unused_pwr_seq_h; assign unused_pwr_seq_h = pwr_seq_h_i; @@ -60,8 +65,8 @@ module prim_generic_otp wire unused_ext_voltage; assign unused_ext_voltage = ext_voltage_io; - logic unused_ext_voltage_en; - assign unused_ext_voltage_en = ext_voltage_en_i; + logic unused_test_ctrl_i; + assign unused_test_ctrl_i = ^test_ctrl_i; logic unused_scan; assign unused_scan = ^{scanmode_i, scan_en_i, scan_rst_ni}; @@ -160,18 +165,18 @@ module prim_generic_otp logic valid_d, valid_q; logic req, wren, rvalid; logic [1:0] rerror; - logic [Width-1:0] rdata_d; - logic [2**SizeWidth-1:0][Width-1:0] rdata_q, wdata_q; logic [AddrWidth-1:0] addr_q; logic [SizeWidth-1:0] size_q; logic [SizeWidth-1:0] cnt_d, cnt_q; logic cnt_clr, cnt_en; + logic read_ecc_on; + logic wdata_inconsistent; + assign cnt_d = (cnt_clr) ? '0 : (cnt_en) ? cnt_q + 1'b1 : cnt_q; assign valid_o = valid_q; - assign rdata_o = rdata_q; assign err_o = err_q; always_comb begin : p_fsm @@ -184,6 +189,7 @@ module prim_generic_otp wren = 1'b0; cnt_clr = 1'b0; cnt_en = 1'b0; + read_ecc_on = 1'b1; unique case (state_q) // Wait here until we receive an initialization command. @@ -193,10 +199,6 @@ module prim_generic_otp if (valid_i) begin if (cmd_i == Init) begin state_d = InitSt; - end else begin - // Invalid commands get caught here - valid_d = 1'b1; - err_d = MacroError; end end end @@ -216,11 +218,7 @@ module prim_generic_otp unique case (cmd_i) Read: state_d = ReadSt; Write: state_d = WriteCheckSt; - default: begin - // Invalid commands get caught here - valid_d = 1'b1; - err_d = MacroError; - end + default: ; endcase // cmd_i end end @@ -252,37 +250,39 @@ module prim_generic_otp end end end - // First, perform a blank check. + // First, read out to perform the write blank check and + // read-modify-write operation. WriteCheckSt: begin state_d = WriteWaitSt; req = 1'b1; + // Register raw memory contents without correction + read_ecc_on = 1'b0; end // Wait for readout to complete first. - // If the write data would clear an already programmed bit, or if we got an uncorrectable - // ECC error, the check has failed and we abort the write at this point. WriteWaitSt: begin + // Register raw memory contents without correction + read_ecc_on = 1'b0; if (rvalid) begin cnt_en = 1'b1; - // TODO: this blank check needs to be extended to account for the ECC bits as well. - if (rerror[1] || (rdata_d & wdata_q[cnt_q]) != rdata_d) begin - state_d = IdleSt; - valid_d = 1'b1; - err_d = MacroWriteBlankError; + + if (cnt_q == size_q) begin + cnt_clr = 1'b1; + state_d = WriteSt; end else begin - if (cnt_q == size_q) begin - cnt_clr = 1'b1; - state_d = WriteSt; - end else begin - state_d = WriteCheckSt; - end + state_d = WriteCheckSt; end end end - // Now that the write check was successful, we can write all native words in one go. + // If the write data attempts to clear an already programmed bit, + // the MacroWriteBlankError needs to be asserted. WriteSt: begin req = 1'b1; wren = 1'b1; cnt_en = 1'b1; + if (wdata_inconsistent) begin + err_d = MacroWriteBlankError; + end + if (cnt_q == size_q) begin valid_d = 1'b1; state_d = IdleSt; @@ -301,27 +301,59 @@ module prim_generic_otp logic [AddrWidth-1:0] addr; assign addr = addr_q + AddrWidth'(cnt_q); + logic [Width-1:0] rdata_corr; + logic [Width+EccWidth-1:0] rdata_d, wdata_ecc, rdata_ecc, wdata_rmw; + logic [2**SizeWidth-1:0][Width-1:0] wdata_q, rdata_reshaped; + logic [2**SizeWidth-1:0][Width+EccWidth-1:0] rdata_q; + + // Use a standard Hamming ECC for OTP. + prim_secded_hamming_22_16_enc u_enc ( + .data_i(wdata_q[cnt_q]), + .data_o(wdata_ecc) + ); + + prim_secded_hamming_22_16_dec u_dec ( + .data_i (rdata_ecc), + .data_o (rdata_corr), + .syndrome_o ( ), + .err_o (rerror) + ); + + assign rdata_d = (read_ecc_on) ? {{EccWidth{1'b0}}, rdata_corr} + : rdata_ecc; + + // Read-modify-write (OTP can only set bits to 1, but not clear to 0). + assign wdata_rmw = wdata_ecc | rdata_q[cnt_q]; + // This indicates if the write data is inconsistent (i.e., if the operation attempts to + // clear an already programmed bit to zero). + assign wdata_inconsistent = (rdata_q[cnt_q] & wdata_ecc) != rdata_q[cnt_q]; + + // Output data without ECC bits. + always_comb begin : p_output_map + for (int k = 0; k < 2**SizeWidth; k++) begin + rdata_reshaped[k] = rdata_q[k][Width-1:0]; + end + rdata_o = rdata_reshaped; + end + prim_ram_1p_adv #( .Depth (Depth), - .Width (Width), + .Width (Width + EccWidth), .MemInitFile (MemInitFile), - .EnableECC (1'b1), .EnableInputPipeline (1), - .EnableOutputPipeline (1), - // Use a standard Hamming ECC for OTP. - .HammingECC (1) + .EnableOutputPipeline (1) ) u_prim_ram_1p_adv ( .clk_i, .rst_ni, - .req_i ( req ), - .write_i ( wren ), - .addr_i ( addr ), - .wdata_i ( wdata_q[cnt_q] ), - .wmask_i ( {Width{1'b1}} ), - .rdata_o ( rdata_d ), - .rvalid_o ( rvalid ), - .rerror_o ( rerror ), - .cfg_i ( '0 ) + .req_i ( req ), + .write_i ( wren ), + .addr_i ( addr ), + .wdata_i ( wdata_rmw ), + .wmask_i ( {Width+EccWidth{1'b1}} ), + .rdata_o ( rdata_ecc ), + .rvalid_o ( rvalid ), + .rerror_o ( ), + .cfg_i ( '0 ) ); // Currently it is assumed that no wrap arounds can occur. @@ -369,4 +401,13 @@ module prim_generic_otp end end + //////////////// + // Assertions // + //////////////// + + // Check that the otp_ctrl FSMs only issue legal commands to the wrapper. + `ASSERT(CheckCommands0_A, state_q == ResetSt && valid_i && ready_o |-> cmd_i == Init) + `ASSERT(CheckCommands1_A, state_q != ResetSt && valid_i && ready_o |-> cmd_i inside {Read, Write}) + + endmodule : prim_generic_otp diff --git a/vendor/lowrisc_ip/ip/prim_xilinx/rtl/prim_xilinx_clock_gating.sv b/vendor/lowrisc_ip/ip/prim_xilinx/rtl/prim_xilinx_clock_gating.sv index daf2e1d8..23c6257f 100644 --- a/vendor/lowrisc_ip/ip/prim_xilinx/rtl/prim_xilinx_clock_gating.sv +++ b/vendor/lowrisc_ip/ip/prim_xilinx/rtl/prim_xilinx_clock_gating.sv @@ -14,7 +14,9 @@ module prim_xilinx_clock_gating #( if (NoFpgaGate) begin : gen_no_gate assign clk_o = clk_i; end else begin : gen_gate - BUFGCE u_bufgce ( + BUFGCE #( + .SIM_DEVICE("7SERIES") + ) u_bufgce ( .I (clk_i), .CE(en_i | test_en_i), .O (clk_o) diff --git a/vendor/lowrisc_ip/lint/tools/dvsim/common_lint_cfg.hjson b/vendor/lowrisc_ip/lint/tools/dvsim/common_lint_cfg.hjson index 1676a0e2..76a39c59 100644 --- a/vendor/lowrisc_ip/lint/tools/dvsim/common_lint_cfg.hjson +++ b/vendor/lowrisc_ip/lint/tools/dvsim/common_lint_cfg.hjson @@ -19,7 +19,7 @@ build_log: "{build_dir}/{tool}.log" // We rely on fusesoc to run lint for us - build_cmd: "fusesoc" + build_cmd: "{job_prefix} fusesoc" build_opts: ["--cores-root {proj_root}", "run", "--flag=fileset_{design_level}", diff --git a/vendor/lowrisc_ip/util/dvsim/Deploy.py b/vendor/lowrisc_ip/util/dvsim/Deploy.py index 34b9ed54..dac9788b 100644 --- a/vendor/lowrisc_ip/util/dvsim/Deploy.py +++ b/vendor/lowrisc_ip/util/dvsim/Deploy.py @@ -73,9 +73,8 @@ class Deploy(): # Construct the job's command. self.cmd = self._construct_cmd() - # Create the launcher object. Launcher retains the handle to self for - # lookup & callbacks. - self.launcher = get_launcher(self) + # Launcher instance created later using create_launcher() method. + self.launcher = None def _define_attrs(self): """Defines the attributes this instance needs to have. @@ -132,6 +131,9 @@ class Deploy(): """ self._extract_attrs(self.sim_cfg.__dict__) + # Enable GUI mode. + self.gui = self.sim_cfg.gui + # Output directory where the artifacts go (used by the launcher). self.odir = getattr(self, self.target + "_dir") @@ -277,6 +279,15 @@ class Deploy(): return "{}/{}.log".format(self.odir, self.target) + def create_launcher(self): + """Creates the launcher instance. + + Note that the launcher instance for ALL jobs in the same job group must + be created before the Scheduler starts to dispatch one by one. + """ + # Retain the handle to self for lookup & callbacks. + self.launcher = get_launcher(self) + class CompileSim(Deploy): """Abstraction for building the simulation executable.""" @@ -318,6 +329,9 @@ class CompileSim(Deploy): super()._extract_attrs(self.build_mode_obj.__dict__) super()._set_attrs() + # Dont run the compile job in GUI mode. + self.gui = False + # 'build_mode' is used as a substitution variable in the HJson. self.build_mode = self.name self.job_name += f"_{self.build_mode}" @@ -399,8 +413,6 @@ class RunTest(Deploy): # arg's name. assert self.build_mode == build_job.name - self.launcher.renew_odir = True - def _define_attrs(self): super()._define_attrs() self.mandatory_cmd_attrs.update({ @@ -437,13 +449,16 @@ class RunTest(Deploy): self.full_name = self.sim_cfg.name + ":" + self.qual_name self.job_name += f"_{self.build_mode}" if self.sim_cfg.cov: - self.output_dirs += [self.cov_db_test_dir] + self.output_dirs += [self.cov_db_dir] # In GUI mode, the log file is not updated; hence, nothing to check. - if not self.sim_cfg.gui: + if not self.gui: self.pass_patterns = self.run_pass_patterns self.fail_patterns = self.run_fail_patterns + def pre_launch(self): + self.launcher.renew_odir = True + def post_finish(self, status): if status != 'P': # Delete the coverage data if available. @@ -618,9 +633,6 @@ class CovReport(Deploy): tablefmt="pipe", colalign=colalign) - # Delete the cov report - not needed. - rm_path(self.get_log_path()) - class CovAnalyze(Deploy): """Abstraction for running the coverage analysis tool.""" diff --git a/vendor/lowrisc_ip/util/dvsim/FlowCfg.py b/vendor/lowrisc_ip/util/dvsim/FlowCfg.py index 498fa622..809f0bba 100644 --- a/vendor/lowrisc_ip/util/dvsim/FlowCfg.py +++ b/vendor/lowrisc_ip/util/dvsim/FlowCfg.py @@ -14,8 +14,8 @@ import hjson from CfgJson import set_target_attribute from LauncherFactory import get_launcher_cls from Scheduler import Scheduler -from utils import (VERBOSE, find_and_substitute_wildcards, md_results_to_html, - rm_path, subst_wildcards) +from utils import (VERBOSE, clean_odirs, find_and_substitute_wildcards, md_results_to_html, + mk_path, rm_path, subst_wildcards) # Interface class for extensions. @@ -53,6 +53,7 @@ class FlowCfg(): # Options set from hjson cfg. self.project = "" self.scratch_path = "" + self.scratch_base_path = "" # Add exports using 'exports' keyword - these are exported to the child # process' environment. @@ -88,6 +89,9 @@ class FlowCfg(): self.results_server_cmd = "" self.css_file = os.path.join( os.path.dirname(os.path.realpath(__file__)), "style.css") + # `self.results_path` will be updated after `self.rel_path` and + # `self.scratch_base_root` variables are updated. + self.results_path = "" self.results_server_path = "" self.results_server_dir = "" self.results_server_html = "" @@ -128,6 +132,9 @@ class FlowCfg(): self.rel_path = os.path.dirname(self.flow_cfg_file).replace( self.proj_root + '/', '') + self.results_path = os.path.join(self.scratch_base_path, "reports", self.rel_path, + self.timestamp) + # Process overrides before substituting wildcards self._process_overrides() @@ -394,12 +401,15 @@ class FlowCfg(): ''' for item in self.cfgs: result = item._gen_results(results) + item.write_results_html() log.info("[results]: [%s]:\n%s\n", item.name, result) log.info("[scratch_path]: [%s] [%s]", item.name, item.scratch_path) + log.log(VERBOSE, "[results_path]: [%s] [%s]", item.name, item.results_path) self.errors_seen |= item.errors_seen if self.is_primary_cfg: self.gen_results_summary() + self.write_results_html() self.gen_email_html_summary() def gen_results_summary(self): @@ -407,6 +417,25 @@ class FlowCfg(): ''' return + def write_results_html(self): + # Prepare workspace to generate reports. + # Keep up to 2 weeks results. + clean_odirs(odir=self.results_path, max_odirs=14) + mk_path(self.results_path) + + # Write results to the report area. + if self.is_primary_cfg: + results_html = md_results_to_html(self.results_title, self.css_file, + self.results_summary_md) + result_path = os.path.join(self.results_path, "summary.html") + else: + results_html = md_results_to_html(self.results_title, self.css_file, + self.results_md) + result_path = os.path.join(self.results_path, "results.html") + with open(result_path, "w") as results_file: + results_file.write(results_html) + log.log(VERBOSE, "[results page]: [%s][%s], self.name, results_path") + def _get_results_page_link(self, link_text): if not self.args.publish: return link_text @@ -549,8 +578,8 @@ class FlowCfg(): publish_results_md = publish_results_md + history_txt # Publish the results page. - # First, write the results html file temporarily to the scratch area. - results_html_file = self.scratch_path + "/results_" + self.timestamp + \ + # First, write the results html file to the scratch area. + results_html_file = self.results_path + "/publish_results_" + self.timestamp + \ ".html" f = open(results_html_file, 'w') f.write( @@ -570,7 +599,6 @@ class FlowCfg(): log.log(VERBOSE, cmd_output.stdout.decode("utf-8")) except Exception as e: log.error("%s: Failed to publish results:\n\"%s\"", e, str(cmd)) - rm_path(results_html_file) def publish_results(self): '''Public facing API for publishing results to the opentitan web @@ -586,20 +614,13 @@ class FlowCfg(): '''Public facing API for publishing md format results to the opentitan web server. ''' - results_html_file = "summary_" + self.timestamp + ".html" results_page_url = self.results_summary_server_page.replace( self.results_server_prefix, self.results_server_url_prefix) # Publish the results page. - # First, write the results html file temporarily to the scratch area. - f = open(results_html_file, 'w') - f.write( - md_results_to_html(self.results_title, self.css_file, - self.results_summary_md)) - f.close() - log.info("Publishing results summary to %s", results_page_url) - cmd = (self.results_server_cmd + " cp " + results_html_file + " " + + result_summary_path = os.path.join(self.results_path, "summary.html") + cmd = (self.results_server_cmd + " cp " + result_summary_path + " " + self.results_summary_server_page) log.log(VERBOSE, cmd) try: @@ -610,7 +631,6 @@ class FlowCfg(): log.log(VERBOSE, cmd_output.stdout.decode("utf-8")) except Exception as e: log.error("%s: Failed to publish results:\n\"%s\"", e, str(cmd)) - rm_path(results_html_file) def has_errors(self): return self.errors_seen diff --git a/vendor/lowrisc_ip/util/dvsim/FormalCfg.py b/vendor/lowrisc_ip/util/dvsim/FormalCfg.py index 6568e330..7e01ce8b 100644 --- a/vendor/lowrisc_ip/util/dvsim/FormalCfg.py +++ b/vendor/lowrisc_ip/util/dvsim/FormalCfg.py @@ -272,16 +272,11 @@ class FormalCfg(OneShotCfg): if messages is not None: results_str += self.parse_dict_to_str(messages) - # Write results to the scratch area self.results_md = results_str - results_file = self.scratch_path + "/results_" + self.timestamp + ".md" - with open(results_file, 'w') as f: - f.write(self.results_md) # Generate result summary self.result_summary[self.name] = summary - log.log(VERBOSE, "[results page]: [%s] [%s]", self.name, results_file) return self.results_md def _publish_results(self): diff --git a/vendor/lowrisc_ip/util/dvsim/LintCfg.py b/vendor/lowrisc_ip/util/dvsim/LintCfg.py index f1f460c3..f171d9bd 100644 --- a/vendor/lowrisc_ip/util/dvsim/LintCfg.py +++ b/vendor/lowrisc_ip/util/dvsim/LintCfg.py @@ -218,10 +218,4 @@ class LintCfg(OneShotCfg): self.email_results_md = self.results_md self.publish_results_md = self.results_md - # Write results to the scratch area - results_file = self.scratch_path + "/results_" + self.timestamp + ".md" - with open(results_file, 'w') as f: - f.write(self.results_md) - - log.log(VERBOSE, "[results page]: [%s] [%s]", self.name, results_file) return self.results_md diff --git a/vendor/lowrisc_ip/util/dvsim/Scheduler.py b/vendor/lowrisc_ip/util/dvsim/Scheduler.py index 8fe45536..ad880ec8 100644 --- a/vendor/lowrisc_ip/util/dvsim/Scheduler.py +++ b/vendor/lowrisc_ip/util/dvsim/Scheduler.py @@ -108,6 +108,10 @@ class Scheduler: # per-target. self.item_to_status = {} + # Create the launcher instance for all items. + for item in self.items: + item.create_launcher() + # The chosen launcher class. This allows us to access launcher # variant-specific settings such as max parallel jobs & poll rate. self.launcher_cls = launcher_cls diff --git a/vendor/lowrisc_ip/util/dvsim/SimCfg.py b/vendor/lowrisc_ip/util/dvsim/SimCfg.py index 66a882bc..dd816509 100644 --- a/vendor/lowrisc_ip/util/dvsim/SimCfg.py +++ b/vendor/lowrisc_ip/util/dvsim/SimCfg.py @@ -671,14 +671,6 @@ class SimCfg(FlowCfg): results_str += "\n".join(create_bucket_report(results.buckets)) self.results_md = results_str - - # Write results to the scratch area - results_path = self.scratch_path + "/results_" + self.timestamp + ".md" - with open(results_path, 'w') as results_file: - results_file.write(self.results_md) - - # Return only the tables - log.log(VERBOSE, "[results page]: [%s] [%s]", self.name, results_path) return results_str def gen_results_summary(self): diff --git a/vendor/lowrisc_ip/util/dvsim/SynCfg.py b/vendor/lowrisc_ip/util/dvsim/SynCfg.py index 719e0fe9..2c35248a 100644 --- a/vendor/lowrisc_ip/util/dvsim/SynCfg.py +++ b/vendor/lowrisc_ip/util/dvsim/SynCfg.py @@ -392,10 +392,4 @@ class SynCfg(OneShotCfg): # TODO: add support for pie / bar charts for area splits and # QoR history - # Write results to the scratch area - results_file = self.scratch_path + "/results_" + self.timestamp + ".md" - with open(results_file, 'w') as f: - f.write(self.results_md) - - log.log(VERBOSE, "[results page]: [%s] [%s]", self.name, results_file) return self.results_md diff --git a/vendor/lowrisc_ip/util/dvsim/Testplan.py b/vendor/lowrisc_ip/util/dvsim/Testplan.py index 9d430be8..c859f711 100644 --- a/vendor/lowrisc_ip/util/dvsim/Testplan.py +++ b/vendor/lowrisc_ip/util/dvsim/Testplan.py @@ -308,10 +308,20 @@ class Testplan(): os.path.join(self_path, os.pardir, os.pardir)) obj = Testplan._parse_hjson(filename) + + parsed = set() imported_testplans = obj.get("import_testplans", []) - for imported_testplan in imported_testplans: - path = os.path.join(repo_top, imported_testplan) - obj = _merge_dicts(obj, self._parse_hjson(path)) + while imported_testplans: + testplan = imported_testplans.pop(0) + if testplan in parsed: + print(f"Error: encountered the testplan {testplan} again, " + "which was already parsed. Please check for circular " + "dependencies.") + sys.exit(1) + parsed.add(testplan) + data = self._parse_hjson(os.path.join(repo_top, testplan)) + imported_testplans.extend(data.get("import_testplans", [])) + obj = _merge_dicts(obj, data) self.name = obj.get("name") @@ -347,7 +357,7 @@ class Testplan(): regressions = defaultdict(set) for tp in self.testpoints: if tp.milestone in tp.milestones[1:]: - regressions[tp.milestone].union({t for t in tp.tests if t}) + regressions[tp.milestone].update({t for t in tp.tests if t}) # Build regressions dict into a hjson like data structure return [{ @@ -355,46 +365,57 @@ class Testplan(): "tests": list(regressions[ms]) } for ms in regressions] - def get_testplan_table(self, fmt="md"): + def get_testplan_table(self, fmt="pipe"): """Generate testplan table from hjson testplan. - fmt is either 'md' or 'html'. + fmt is either 'pipe' (markdown) or 'html'. 'pipe' is the name used by + tabulate to generate a markdown formatted table. """ - assert fmt in ["md", "html"] + assert fmt in ["pipe", "html"] + + def _fmt_text(text, fmt): + return mistletoe.markdown(text) if fmt == "html" else text if self.testpoints: - text = "### Testpoints\n\n" + lines = [_fmt_text("\n### Testpoints\n", fmt)] header = ["Milestone", "Name", "Tests", "Description"] colalign = ("center", "center", "left", "left") table = [] for tp in self.testpoints: - desc = tp.desc.strip() - tests = "\\\n".join(tp.tests) + desc = _fmt_text(tp.desc.strip(), fmt) + # TODO(astanin/python-tabulate#126): Tabulate does not + # convert \n's to line-breaks. + tests = "
\n".join(tp.tests) table.append([tp.milestone, tp.name, tests, desc]) - text += tabulate(table, - headers=header, - tablefmt=fmt, - colalign=colalign) - text += "\n" + lines += [ + tabulate(table, + headers=header, + tablefmt=fmt, + colalign=colalign) + ] if self.covergroups: - text += "\n### Covergroups\n\n" + lines += [_fmt_text("\n### Covergroups\n", fmt)] header = ["Name", "Description"] colalign = ("center", "left") table = [] for covergroup in self.covergroups: - desc = covergroup.desc.strip() + desc = _fmt_text(covergroup.desc.strip(), fmt) table.append([covergroup.name, desc]) - text += tabulate(table, - headers=header, - tablefmt="pipe", - colalign=colalign) - text += "\n" + lines += [ + tabulate(table, + headers=header, + tablefmt=fmt, + colalign=colalign) + ] + text = "\n".join(lines) if fmt == "html": - text = self.get_dv_style_css() + mistletoe.markdown(text) + text = self.get_dv_style_css() + text text = text.replace("", "
") + # Tabulate does not support HTML tags. + text = text.replace("<", "<").replace(">", ">") return text def map_test_results(self, test_results): @@ -614,6 +635,8 @@ class Testplan(): The data extracted from the sim_results table HJson file is mapped into a test results, test progress, covergroup progress and coverage tables. + + fmt is either 'md' (markdown) or 'html'. """ assert fmt in ["md", "html"] sim_results = Testplan._parse_hjson(sim_results_file) diff --git a/vendor/lowrisc_ip/util/dvsim/examples/testplanner/common_testplan.hjson b/vendor/lowrisc_ip/util/dvsim/examples/testplanner/common_testplan.hjson index 5a5f2883..e6f71d3e 100644 --- a/vendor/lowrisc_ip/util/dvsim/examples/testplanner/common_testplan.hjson +++ b/vendor/lowrisc_ip/util/dvsim/examples/testplanner/common_testplan.hjson @@ -2,8 +2,7 @@ // Licensed under the Apache License, Version 2.0, see LICENSE for details. // SPDX-License-Identifier: Apache-2.0 { - // only 'entries' supported in imported testplans for now - entries: [ + testpoints: [ { name: csr desc: '''Standard CSR suite of tests run from all valid interfaces to prove SW @@ -13,10 +12,10 @@ // importer needs to provide substitutions for these as string or a list // if list, then substitution occurs on all values in the list // if substitution is not provided, it will be replaced with an empty string - tests: ["{name}{intf}_csr_hw_reset", - "{name}{intf}_csr_rw", - "{name}{intf}_csr_bit_bash", - "{name}{intf}_csr_aliasing",] + tests: ["{name}_{intf}_csr_hw_reset", + "{name}_{intf}_csr_rw", + "{name}_{intf}_csr_bit_bash", + "{name}_{intf}_csr_aliasing",] } ] } diff --git a/vendor/lowrisc_ip/util/dvsim/examples/testplanner/foo_testplan.hjson b/vendor/lowrisc_ip/util/dvsim/examples/testplanner/foo_testplan.hjson index 920400de..959b1086 100644 --- a/vendor/lowrisc_ip/util/dvsim/examples/testplanner/foo_testplan.hjson +++ b/vendor/lowrisc_ip/util/dvsim/examples/testplanner/foo_testplan.hjson @@ -29,7 +29,7 @@ desc: "A single line description with single double-inverted commas." milestone: V2 // testplan entry with no tests added - tests: ["{name}_feature1_{intf}"] + tests: ["{name}_{intf}_feature1"] } { name: feature2 diff --git a/vendor/lowrisc_ip/util/dvsim/utils.py b/vendor/lowrisc_ip/util/dvsim/utils.py index dedcf752..4916a8db 100644 --- a/vendor/lowrisc_ip/util/dvsim/utils.py +++ b/vendor/lowrisc_ip/util/dvsim/utils.py @@ -555,6 +555,20 @@ def rm_path(path, ignore_error=False): raise exc +def mk_path(path): + '''Create the specified path if it does not exist. + + 'path' is a Path-like object. If it does exist, the function simply + returns. If it does not exist, the function creates the path and its + parent dictories if necessary. + ''' + try: + Path(path).mkdir(parents=True, exist_ok=True) + except PermissionError as e: + log.fatal("Failed to create dirctory {}:\n{}.".format(path, e)) + sys.exit(1) + + def clean_odirs(odir, max_odirs, ts_format=TS_FORMAT): """Clean previous output directories. diff --git a/vendor/lowrisc_ip/util/uvmdvgen/index.md.tpl b/vendor/lowrisc_ip/util/uvmdvgen/index.md.tpl index 336b7faf..15f04a3c 100644 --- a/vendor/lowrisc_ip/util/uvmdvgen/index.md.tpl +++ b/vendor/lowrisc_ip/util/uvmdvgen/index.md.tpl @@ -64,11 +64,10 @@ This provides the ability to drive and independently monitor random traffic via % endif % if has_alerts: -${'###'} Alert agents +${'###'} Alert_agents ${name.upper()} testbench instantiates (already handled in CIP base env) [alert_agents]({{< relref "hw/dv/sv/alert_esc_agent/README.md" >}}): [list alert names]. -The alert_agents provide the ability to drive and independently monitor alert handshakes via -alert interfaces in ${name.upper()} device. +The alert_agents provide the ability to drive and independently monitor alert handshakes via alert interfaces in ${name.upper()} device. % endif % for agent in env_agents: diff --git a/vendor/lowrisc_ip/util/uvmdvgen/scoreboard.sv.tpl b/vendor/lowrisc_ip/util/uvmdvgen/scoreboard.sv.tpl index 41017cbc..de5a3bf0 100644 --- a/vendor/lowrisc_ip/util/uvmdvgen/scoreboard.sv.tpl +++ b/vendor/lowrisc_ip/util/uvmdvgen/scoreboard.sv.tpl @@ -76,7 +76,7 @@ class ${name}_scoreboard extends dv_base_scoreboard #( bit data_phase_write = (write && channel == DataChannel); // if access was to a valid csr, get the csr handle - if (csr_addr inside {cfg.csr_addrs[ral_name]}) begin + if (csr_addr inside {cfg.ral_models[ral_name].csr_addrs}) begin csr = ral.default_map.get_reg_by_offset(csr_addr); `DV_CHECK_NE_FATAL(csr, null) end diff --git a/vendor/lowrisc_ip/util/uvmdvgen/testplan.hjson.tpl b/vendor/lowrisc_ip/util/uvmdvgen/testplan.hjson.tpl index d8fff33b..0ba2bd77 100644 --- a/vendor/lowrisc_ip/util/uvmdvgen/testplan.hjson.tpl +++ b/vendor/lowrisc_ip/util/uvmdvgen/testplan.hjson.tpl @@ -8,7 +8,7 @@ "hw/dv/tools/dvsim/testplans/mem_testplan.hjson", "hw/dv/tools/dvsim/testplans/intr_test_testplan.hjson", "hw/dv/tools/dvsim/testplans/tl_device_access_types_testplan.hjson"] - entries: [ + testpoints: [ { name: smoke desc: ''' @@ -30,4 +30,11 @@ tests: [] } ] + + covergroups: [ + { + name: ${name}_feature_cg + desc: '''Describe the functionality covered by this covergroup.''' + } + ] }