From fe04fd254cf8f486a14b36a1e327927a228f6b0b Mon Sep 17 00:00:00 2001 From: Samuel Riedel Date: Thu, 18 Dec 2025 01:06:34 +0000 Subject: [PATCH 01/10] [dv] Use `id_done` to accurately track instruction monitor This refactors `instr_vif` to use `rvfi_id_done` instead of `instr_new_id` to track when a new instruction appears in the ID stage. This interface and signal are only used to keep track of instruction fetch errors by using the aforementioned valid signal and checking whether the `rvfi_order_id` has changed. However, if an instruction fetch error is consecutive to another error, `instr_new_id` will be gated, which leads us to miss the `fetch_err` in the verification. This will fix failing `riscv_mem_error_test` --- .../common/ibex_cosim_agent/ibex_cosim_scoreboard.sv | 7 +++---- dv/uvm/core_ibex/env/core_ibex_instr_monitor_if.sv | 4 ++-- dv/uvm/core_ibex/tb/core_ibex_tb_top.sv | 2 +- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/dv/uvm/core_ibex/common/ibex_cosim_agent/ibex_cosim_scoreboard.sv b/dv/uvm/core_ibex/common/ibex_cosim_agent/ibex_cosim_scoreboard.sv index 7998349d82..f55e1936f6 100644 --- a/dv/uvm/core_ibex/common/ibex_cosim_agent/ibex_cosim_scoreboard.sv +++ b/dv/uvm/core_ibex/common/ibex_cosim_agent/ibex_cosim_scoreboard.sv @@ -268,8 +268,7 @@ class ibex_cosim_scoreboard extends uvm_scoreboard; bit [31:0] aligned_next_addr; forever begin // Wait for new instruction to appear in ID stage - wait (instr_vif.instr_cb.valid_id && - instr_vif.instr_cb.instr_new_id && + wait (instr_vif.instr_cb.rvfi_id_done && latest_order != instr_vif.instr_cb.rvfi_order_id); latest_order = instr_vif.instr_cb.rvfi_order_id; @@ -320,9 +319,9 @@ class ibex_cosim_scoreboard extends uvm_scoreboard; // Wait for a new instruction or a writeback exception. When a new instruction has entered the // ID stage and we haven't seen a writeback exception we know the instruction associated with the // error just added to the queue isn't getting flushed. - wait (instr_vif.instr_cb.instr_new_id || dut_vif.dut_cb.wb_exception); + wait (instr_vif.instr_cb.rvfi_id_done || dut_vif.dut_cb.wb_exception); - if (!instr_vif.instr_cb.instr_new_id && dut_vif.dut_cb.wb_exception) begin + if (!instr_vif.instr_cb.rvfi_id_done && dut_vif.dut_cb.wb_exception) begin // If we hit a writeback exception without seeing a new instruction then the newly added // error relates to an instruction just flushed from the ID stage so pop it from the // queue. diff --git a/dv/uvm/core_ibex/env/core_ibex_instr_monitor_if.sv b/dv/uvm/core_ibex/env/core_ibex_instr_monitor_if.sv index 08daae43f6..51e437bbd4 100644 --- a/dv/uvm/core_ibex/env/core_ibex_instr_monitor_if.sv +++ b/dv/uvm/core_ibex/env/core_ibex_instr_monitor_if.sv @@ -15,7 +15,7 @@ interface core_ibex_instr_monitor_if #( // ID stage logic reset; logic valid_id; - logic instr_new_id; + logic rvfi_id_done; logic err_id; logic is_compressed_id; logic [15:0] instr_compressed_id; @@ -30,7 +30,7 @@ interface core_ibex_instr_monitor_if #( clocking instr_cb @(posedge clk); input reset; input valid_id; - input instr_new_id; + input rvfi_id_done; input err_id; input is_compressed_id; input instr_compressed_id; diff --git a/dv/uvm/core_ibex/tb/core_ibex_tb_top.sv b/dv/uvm/core_ibex/tb/core_ibex_tb_top.sv index b4107f41ac..b504b15235 100644 --- a/dv/uvm/core_ibex/tb/core_ibex_tb_top.sv +++ b/dv/uvm/core_ibex/tb/core_ibex_tb_top.sv @@ -274,7 +274,7 @@ module core_ibex_tb_top; // Instruction monitor connections assign instr_monitor_if.reset = ~rst_n; assign instr_monitor_if.valid_id = dut.u_ibex_top.u_ibex_core.id_stage_i.instr_valid_i; - assign instr_monitor_if.instr_new_id = dut.u_ibex_top.u_ibex_core.instr_new_id; + assign instr_monitor_if.rvfi_id_done = dut.u_ibex_top.u_ibex_core.rvfi_id_done; assign instr_monitor_if.err_id = dut.u_ibex_top.u_ibex_core.id_stage_i.controller_i.instr_fetch_err; From 88c1c097e064688cc1127a08041bd319f55c6673 Mon Sep 17 00:00:00 2001 From: Samuel Riedel Date: Thu, 18 Sep 2025 17:49:00 +0000 Subject: [PATCH 02/10] [dv] Enable Zcb and Zcmp extension in compiler --- .../riscv_dv_extension/riscv_core_setting.tpl.sv | 2 +- dv/uvm/core_ibex/riscv_dv_extension/testlist.yaml | 9 +++++++++ dv/uvm/core_ibex/scripts/ibex_cmd.py | 3 ++- dv/uvm/core_ibex/tests/core_ibex_base_test.sv | 4 ++++ 4 files changed, 16 insertions(+), 2 deletions(-) diff --git a/dv/uvm/core_ibex/riscv_dv_extension/riscv_core_setting.tpl.sv b/dv/uvm/core_ibex/riscv_dv_extension/riscv_core_setting.tpl.sv index fff0a1af17..c64ce30238 100644 --- a/dv/uvm/core_ibex/riscv_dv_extension/riscv_core_setting.tpl.sv +++ b/dv/uvm/core_ibex/riscv_dv_extension/riscv_core_setting.tpl.sv @@ -53,7 +53,7 @@ bit support_unaligned_load_store = 1'b1; // ISA supported by the processor // TODO: Determine how Ibex RV32B types map to RISCV-DV ISA names -riscv_instr_group_t supported_isa[$] = {RV32I, RV32M, RV32C +riscv_instr_group_t supported_isa[$] = {RV32I, RV32M, RV32C, RV32ZCB, RV32ZCMP % if ibex_config['RV32B'] == 'ibex_pkg::RV32BNone': }; % else: diff --git a/dv/uvm/core_ibex/riscv_dv_extension/testlist.yaml b/dv/uvm/core_ibex/riscv_dv_extension/testlist.yaml index 8c6516650f..cff5bf0e4d 100644 --- a/dv/uvm/core_ibex/riscv_dv_extension/testlist.yaml +++ b/dv/uvm/core_ibex/riscv_dv_extension/testlist.yaml @@ -1157,3 +1157,12 @@ rtl_test: core_ibex_base_test rtl_params: RV32B: ["ibex_pkg::RV32BFull", "ibex_pkg::RV32BOTEarlGrey", "ibex_pkg::RV32BBalanced"] + +- test: riscv_zcb_balanced_test + desc: > + Random instruction test with zcb instructions in balanced configuration + iterations: 10 + gen_test: riscv_rand_instr_test + gen_opts: > + +enable_zcb_extension=1 + rtl_test: core_ibex_base_test diff --git a/dv/uvm/core_ibex/scripts/ibex_cmd.py b/dv/uvm/core_ibex/scripts/ibex_cmd.py index 0d8c5346e2..21c84b7b4d 100644 --- a/dv/uvm/core_ibex/scripts/ibex_cmd.py +++ b/dv/uvm/core_ibex/scripts/ibex_cmd.py @@ -114,8 +114,9 @@ def get_isas_for_config(cfg: Config) -> Tuple[str, str]: has_bitmanip = cfg.rv32b != 'ibex_pkg::RV32BNone' toolchain_isa = base_isa + ('b' if has_bitmanip else '') + toolchain_isa = toolchain_isa + ('_zicsr_zifencei_zcb_zcmp') - return (toolchain_isa, '_'.join([base_isa] + bitmanip_isa)) + return (toolchain_isa, '_'.join([base_isa] + ['Zicsr','Zifencei','Zcb','Zcmp'] + bitmanip_isa)) _TestEntry = Dict[str, object] diff --git a/dv/uvm/core_ibex/tests/core_ibex_base_test.sv b/dv/uvm/core_ibex/tests/core_ibex_base_test.sv index 47847ad671..668843b0e6 100644 --- a/dv/uvm/core_ibex/tests/core_ibex_base_test.sv +++ b/dv/uvm/core_ibex/tests/core_ibex_base_test.sv @@ -70,6 +70,10 @@ class core_ibex_base_test extends uvm_test; isa = {"rv32", RV32E ? "e" : "i"}; if (RV32M != RV32MNone) isa = {isa, "m"}; isa = {isa, "c"}; + isa = {isa, "_Zicsr"}; + isa = {isa, "_Zifencei"}; + isa = {isa, "_Zcb"}; + isa = {isa, "_Zcmp"}; case (RV32B) RV32BNone: ; From 00f9fe090d877f5f94643429c8e88a94565b6005 Mon Sep 17 00:00:00 2001 From: Samuel Riedel Date: Thu, 18 Sep 2025 13:28:43 +0000 Subject: [PATCH 03/10] [dv] Pass expanded instruction to cosim --- dv/cosim/cosim.h | 8 ++- dv/cosim/cosim_dpi.cc | 8 ++- dv/cosim/cosim_dpi.h | 5 +- dv/cosim/cosim_dpi.svh | 3 +- dv/cosim/spike_cosim.cc | 4 +- dv/cosim/spike_cosim.h | 3 +- .../ibex_cosim_agent/ibex_cosim_scoreboard.sv | 4 +- .../ibex_cosim_agent/ibex_rvfi_monitor.sv | 33 +++++----- .../ibex_cosim_agent/ibex_rvfi_seq_item.sv | 7 ++ dv/uvm/core_ibex/env/core_ibex_rvfi_if.sv | 7 ++ dv/uvm/core_ibex/tb/core_ibex_tb_top.sv | 65 ++++++++++--------- .../ibex_simple_system_cosim_checker.sv | 3 +- 12 files changed, 95 insertions(+), 55 deletions(-) diff --git a/dv/cosim/cosim.h b/dv/cosim/cosim.h index 4a5c63c75b..13e8920f47 100644 --- a/dv/cosim/cosim.h +++ b/dv/cosim/cosim.h @@ -74,9 +74,15 @@ class Cosim { // In this case the instruction doesn't retire so no register write occurs (so // `write_reg` must be 0). // + // `expanded_insn_valid` is the current instruction an expanded one + // `expanded_insn` is the 32-bit instruction that is being expanded + // `expanded_insn_last` whether this is the last op of an expanded instruction + // // Returns false if there are any errors; use `get_errors` to obtain details virtual bool step(uint32_t write_reg, uint32_t write_reg_data, uint32_t pc, - bool sync_trap, bool suppress_reg_write) = 0; + bool sync_trap, bool suppress_reg_write, + bool expanded_insn_valid, uint32_t expanded_insn, + bool expanded_insn_last) = 0; // When more than one of `set_mip`, `set_nmi` or `set_debug_req` is called // before `step` which one takes effect is chosen by the co-simulator. Which diff --git a/dv/cosim/cosim_dpi.cc b/dv/cosim/cosim_dpi.cc index 30a3da74dd..f3466d489b 100644 --- a/dv/cosim/cosim_dpi.cc +++ b/dv/cosim/cosim_dpi.cc @@ -12,11 +12,15 @@ int riscv_cosim_step(Cosim *cosim, const svBitVecVal *write_reg, const svBitVecVal *write_reg_data, const svBitVecVal *pc, - svBit sync_trap, svBit suppress_reg_write) { + svBit sync_trap, svBit suppress_reg_write, + svBit expanded_insn_valid, + const svBitVecVal *expanded_insn, + svBit expanded_insn_last) { assert(cosim); return cosim->step(write_reg[0], write_reg_data[0], pc[0], sync_trap, - suppress_reg_write) + suppress_reg_write, expanded_insn_valid, expanded_insn[0], + expanded_insn_last) ? 1 : 0; } diff --git a/dv/cosim/cosim_dpi.h b/dv/cosim/cosim_dpi.h index bbadbc5e3c..9ead67385b 100644 --- a/dv/cosim/cosim_dpi.h +++ b/dv/cosim/cosim_dpi.h @@ -16,7 +16,10 @@ extern "C" { int riscv_cosim_step(Cosim *cosim, const svBitVecVal *write_reg, const svBitVecVal *write_reg_data, const svBitVecVal *pc, - svBit sync_trap, svBit suppress_reg_write); + svBit sync_trap, svBit suppress_reg_write, + svBit expanded_insn_valid, + const svBitVecVal *expanded_insn, + svBit expanded_insn_last); void riscv_cosim_set_mip(Cosim *cosim, const svBitVecVal *pre_mip, const svBitVecVal *post_mip); void riscv_cosim_set_nmi(Cosim *cosim, svBit nmi); diff --git a/dv/cosim/cosim_dpi.svh b/dv/cosim/cosim_dpi.svh index 35ecd3b8cb..33f8babe87 100644 --- a/dv/cosim/cosim_dpi.svh +++ b/dv/cosim/cosim_dpi.svh @@ -11,7 +11,8 @@ `define COSIM_DPI_SVH import "DPI-C" function int riscv_cosim_step(chandle cosim_handle, bit [4:0] write_reg, - bit [31:0] write_reg_data, bit [31:0] pc, bit sync_trap, bit suppress_reg_write); + bit [31:0] write_reg_data, bit [31:0] pc, bit sync_trap, bit suppress_reg_write, + bit expanded_insn_valid, bit [15:0] expanded_insn, bit expanded_insn_last); import "DPI-C" function void riscv_cosim_set_mip(chandle cosim_handle, bit [31:0] pre_mip, bit [31:0] post_mip); import "DPI-C" function void riscv_cosim_set_nmi(chandle cosim_handle, bit nmi); diff --git a/dv/cosim/spike_cosim.cc b/dv/cosim/spike_cosim.cc index daa400709b..1ac20483b7 100644 --- a/dv/cosim/spike_cosim.cc +++ b/dv/cosim/spike_cosim.cc @@ -170,7 +170,9 @@ bool SpikeCosim::backdoor_read_mem(uint32_t addr, size_t len, // processor, and when we call step() again we start executing in the new // context of the trap (trap handler, new MSTATUS, debug rom, etc. etc.) bool SpikeCosim::step(uint32_t write_reg, uint32_t write_reg_data, uint32_t pc, - bool sync_trap, bool suppress_reg_write) { + bool sync_trap, bool suppress_reg_write, + bool expanded_insn_valid, uint32_t expanded_insn, + bool expanded_insn_last) { assert(write_reg < 32); // The DUT has just produced an RVFI item diff --git a/dv/cosim/spike_cosim.h b/dv/cosim/spike_cosim.h index 68fd2204fb..abbbff235e 100644 --- a/dv/cosim/spike_cosim.h +++ b/dv/cosim/spike_cosim.h @@ -120,7 +120,8 @@ class SpikeCosim : public simif_t, public Cosim { const uint8_t *data_in) override; bool backdoor_read_mem(uint32_t addr, size_t len, uint8_t *data_out) override; bool step(uint32_t write_reg, uint32_t write_reg_data, uint32_t pc, - bool sync_trap, bool suppress_reg_write) override; + bool sync_trap, bool suppress_reg_write, bool expanded_insn_valid, + uint32_t expanded_insn, bool expanded_insn_last) override; bool check_retired_instr(uint32_t write_reg, uint32_t write_reg_data, uint32_t dut_pc, bool suppress_reg_write); diff --git a/dv/uvm/core_ibex/common/ibex_cosim_agent/ibex_cosim_scoreboard.sv b/dv/uvm/core_ibex/common/ibex_cosim_agent/ibex_cosim_scoreboard.sv index f55e1936f6..deee913ca4 100644 --- a/dv/uvm/core_ibex/common/ibex_cosim_agent/ibex_cosim_scoreboard.sv +++ b/dv/uvm/core_ibex/common/ibex_cosim_agent/ibex_cosim_scoreboard.sv @@ -163,7 +163,9 @@ class ibex_cosim_scoreboard extends uvm_scoreboard; riscv_cosim_set_ic_scr_key_valid(cosim_handle, rvfi_instr.ic_scr_key_valid); if (!riscv_cosim_step(cosim_handle, rvfi_instr.rd_addr, rvfi_instr.rd_wdata, rvfi_instr.pc, - rvfi_instr.trap, rvfi_instr.rf_wr_suppress)) begin + rvfi_instr.trap, rvfi_instr.rf_wr_suppress, + rvfi_instr.expanded_insn_valid, rvfi_instr.expanded_insn, + rvfi_instr.expanded_insn_last)) begin // cosim instruction step doesn't match rvfi captured instruction, report a fatal error // with the details if (cfg.relax_cosim_check) begin diff --git a/dv/uvm/core_ibex/common/ibex_cosim_agent/ibex_rvfi_monitor.sv b/dv/uvm/core_ibex/common/ibex_cosim_agent/ibex_rvfi_monitor.sv index 0bf50116e7..9638d0dcb4 100644 --- a/dv/uvm/core_ibex/common/ibex_cosim_agent/ibex_rvfi_monitor.sv +++ b/dv/uvm/core_ibex/common/ibex_cosim_agent/ibex_rvfi_monitor.sv @@ -30,21 +30,24 @@ class ibex_rvfi_monitor extends uvm_monitor; while(!(vif.monitor_cb.valid || vif.monitor_cb.ext_irq_valid)) vif.wait_clks(1); // Read instruction details from RVFI interface - trans_collected = ibex_rvfi_seq_item::type_id::create("trans_collected"); - trans_collected.irq_only = !vif.monitor_cb.valid && vif.monitor_cb.ext_irq_valid; - trans_collected.trap = vif.monitor_cb.trap; - trans_collected.pc = vif.monitor_cb.pc_rdata; - trans_collected.rd_addr = vif.monitor_cb.rd_addr; - trans_collected.rd_wdata = vif.monitor_cb.rd_wdata; - trans_collected.order = vif.monitor_cb.order; - trans_collected.pre_mip = vif.monitor_cb.ext_pre_mip; - trans_collected.post_mip = vif.monitor_cb.ext_post_mip; - trans_collected.nmi = vif.monitor_cb.ext_nmi; - trans_collected.nmi_int = vif.monitor_cb.ext_nmi_int; - trans_collected.debug_req = vif.monitor_cb.ext_debug_req; - trans_collected.rf_wr_suppress = vif.monitor_cb.ext_rf_wr_suppress; - trans_collected.mcycle = vif.monitor_cb.ext_mcycle; - trans_collected.ic_scr_key_valid = vif.monitor_cb.ext_ic_scr_key_valid; + trans_collected = ibex_rvfi_seq_item::type_id::create("trans_collected"); + trans_collected.irq_only = !vif.monitor_cb.valid && vif.monitor_cb.ext_irq_valid; + trans_collected.trap = vif.monitor_cb.trap; + trans_collected.pc = vif.monitor_cb.pc_rdata; + trans_collected.rd_addr = vif.monitor_cb.rd_addr; + trans_collected.rd_wdata = vif.monitor_cb.rd_wdata; + trans_collected.order = vif.monitor_cb.order; + trans_collected.pre_mip = vif.monitor_cb.ext_pre_mip; + trans_collected.post_mip = vif.monitor_cb.ext_post_mip; + trans_collected.nmi = vif.monitor_cb.ext_nmi; + trans_collected.nmi_int = vif.monitor_cb.ext_nmi_int; + trans_collected.debug_req = vif.monitor_cb.ext_debug_req; + trans_collected.rf_wr_suppress = vif.monitor_cb.ext_rf_wr_suppress; + trans_collected.mcycle = vif.monitor_cb.ext_mcycle; + trans_collected.ic_scr_key_valid = vif.monitor_cb.ext_ic_scr_key_valid; + trans_collected.expanded_insn_valid = vif.monitor_cb.ext_expanded_insn_valid; + trans_collected.expanded_insn = vif.monitor_cb.ext_expanded_insn; + trans_collected.expanded_insn_last = vif.monitor_cb.ext_expanded_insn_last; for (int i=0; i < 10; i++) begin trans_collected.mhpmcounters[i] = vif.monitor_cb.ext_mhpmcounters[i]; diff --git a/dv/uvm/core_ibex/common/ibex_cosim_agent/ibex_rvfi_seq_item.sv b/dv/uvm/core_ibex/common/ibex_cosim_agent/ibex_rvfi_seq_item.sv index dceba31c47..3f7031887f 100644 --- a/dv/uvm/core_ibex/common/ibex_cosim_agent/ibex_rvfi_seq_item.sv +++ b/dv/uvm/core_ibex/common/ibex_cosim_agent/ibex_rvfi_seq_item.sv @@ -21,6 +21,10 @@ class ibex_rvfi_seq_item extends uvm_sequence_item; bit [31:0] mhpmcountersh [10]; bit ic_scr_key_valid; + bit expanded_insn_valid; + bit [15:0] expanded_insn; + bit expanded_insn_last; + `uvm_object_utils_begin(ibex_rvfi_seq_item) `uvm_field_int (trap, UVM_DEFAULT) `uvm_field_int (pc, UVM_DEFAULT) @@ -37,6 +41,9 @@ class ibex_rvfi_seq_item extends uvm_sequence_item; `uvm_field_sarray_int (mhpmcounters, UVM_DEFAULT) `uvm_field_sarray_int (mhpmcountersh, UVM_DEFAULT) `uvm_field_int (ic_scr_key_valid, UVM_DEFAULT) + `uvm_field_int (expanded_insn_valid, UVM_DEFAULT) + `uvm_field_int (expanded_insn, UVM_DEFAULT) + `uvm_field_int (expanded_insn_last, UVM_DEFAULT) `uvm_object_utils_end `uvm_object_new diff --git a/dv/uvm/core_ibex/env/core_ibex_rvfi_if.sv b/dv/uvm/core_ibex/env/core_ibex_rvfi_if.sv index 0199b87f76..b2b0eac1b4 100644 --- a/dv/uvm/core_ibex/env/core_ibex_rvfi_if.sv +++ b/dv/uvm/core_ibex/env/core_ibex_rvfi_if.sv @@ -40,6 +40,10 @@ interface core_ibex_rvfi_if(input logic clk); logic ext_ic_scr_key_valid; + logic ext_expanded_insn_valid; + logic [15:0] ext_expanded_insn; + logic ext_expanded_insn_last; + clocking monitor_cb @(posedge clk); input reset; input valid; @@ -74,6 +78,9 @@ interface core_ibex_rvfi_if(input logic clk); input ext_mhpmcountersh; input ext_ic_scr_key_valid; input ext_irq_valid; + input ext_expanded_insn_valid; + input ext_expanded_insn; + input ext_expanded_insn_last; endclocking task automatic wait_clks(input int num); diff --git a/dv/uvm/core_ibex/tb/core_ibex_tb_top.sv b/dv/uvm/core_ibex/tb/core_ibex_tb_top.sv index b504b15235..c789f65fe4 100644 --- a/dv/uvm/core_ibex/tb/core_ibex_tb_top.sv +++ b/dv/uvm/core_ibex/tb/core_ibex_tb_top.sv @@ -211,37 +211,40 @@ module core_ibex_tb_top; assign instr_mem_vif.be = 0; assign instr_mem_vif.wdata = 0; // RVFI interface connections - assign rvfi_if.reset = ~rst_n; - assign rvfi_if.valid = dut.rvfi_valid; - assign rvfi_if.order = dut.rvfi_order; - assign rvfi_if.insn = dut.rvfi_insn; - assign rvfi_if.trap = dut.rvfi_trap; - assign rvfi_if.intr = dut.rvfi_intr; - assign rvfi_if.mode = dut.rvfi_mode; - assign rvfi_if.ixl = dut.rvfi_ixl; - assign rvfi_if.rs1_addr = dut.rvfi_rs1_addr; - assign rvfi_if.rs2_addr = dut.rvfi_rs2_addr; - assign rvfi_if.rs1_rdata = dut.rvfi_rs1_rdata; - assign rvfi_if.rs2_rdata = dut.rvfi_rs2_rdata; - assign rvfi_if.rd_addr = dut.rvfi_rd_addr; - assign rvfi_if.rd_wdata = dut.rvfi_rd_wdata; - assign rvfi_if.pc_rdata = dut.rvfi_pc_rdata; - assign rvfi_if.pc_wdata = dut.rvfi_pc_wdata; - assign rvfi_if.mem_addr = dut.rvfi_mem_addr; - assign rvfi_if.mem_rmask = dut.rvfi_mem_rmask; - assign rvfi_if.mem_rdata = dut.rvfi_mem_rdata; - assign rvfi_if.mem_wdata = dut.rvfi_mem_wdata; - assign rvfi_if.ext_pre_mip = dut.rvfi_ext_pre_mip; - assign rvfi_if.ext_post_mip = dut.rvfi_ext_post_mip; - assign rvfi_if.ext_nmi = dut.rvfi_ext_nmi; - assign rvfi_if.ext_nmi_int = dut.rvfi_ext_nmi_int; - assign rvfi_if.ext_debug_req = dut.rvfi_ext_debug_req; - assign rvfi_if.ext_rf_wr_suppress = dut.rvfi_ext_rf_wr_suppress; - assign rvfi_if.ext_mcycle = dut.rvfi_ext_mcycle; - assign rvfi_if.ext_mhpmcounters = dut.rvfi_ext_mhpmcounters; - assign rvfi_if.ext_mhpmcountersh = dut.rvfi_ext_mhpmcountersh; - assign rvfi_if.ext_ic_scr_key_valid = dut.rvfi_ext_ic_scr_key_valid; - assign rvfi_if.ext_irq_valid = dut.rvfi_ext_irq_valid; + assign rvfi_if.reset = ~rst_n; + assign rvfi_if.valid = dut.rvfi_valid; + assign rvfi_if.order = dut.rvfi_order; + assign rvfi_if.insn = dut.rvfi_insn; + assign rvfi_if.trap = dut.rvfi_trap; + assign rvfi_if.intr = dut.rvfi_intr; + assign rvfi_if.mode = dut.rvfi_mode; + assign rvfi_if.ixl = dut.rvfi_ixl; + assign rvfi_if.rs1_addr = dut.rvfi_rs1_addr; + assign rvfi_if.rs2_addr = dut.rvfi_rs2_addr; + assign rvfi_if.rs1_rdata = dut.rvfi_rs1_rdata; + assign rvfi_if.rs2_rdata = dut.rvfi_rs2_rdata; + assign rvfi_if.rd_addr = dut.rvfi_rd_addr; + assign rvfi_if.rd_wdata = dut.rvfi_rd_wdata; + assign rvfi_if.pc_rdata = dut.rvfi_pc_rdata; + assign rvfi_if.pc_wdata = dut.rvfi_pc_wdata; + assign rvfi_if.mem_addr = dut.rvfi_mem_addr; + assign rvfi_if.mem_rmask = dut.rvfi_mem_rmask; + assign rvfi_if.mem_rdata = dut.rvfi_mem_rdata; + assign rvfi_if.mem_wdata = dut.rvfi_mem_wdata; + assign rvfi_if.ext_pre_mip = dut.rvfi_ext_pre_mip; + assign rvfi_if.ext_post_mip = dut.rvfi_ext_post_mip; + assign rvfi_if.ext_nmi = dut.rvfi_ext_nmi; + assign rvfi_if.ext_nmi_int = dut.rvfi_ext_nmi_int; + assign rvfi_if.ext_debug_req = dut.rvfi_ext_debug_req; + assign rvfi_if.ext_rf_wr_suppress = dut.rvfi_ext_rf_wr_suppress; + assign rvfi_if.ext_mcycle = dut.rvfi_ext_mcycle; + assign rvfi_if.ext_mhpmcounters = dut.rvfi_ext_mhpmcounters; + assign rvfi_if.ext_mhpmcountersh = dut.rvfi_ext_mhpmcountersh; + assign rvfi_if.ext_ic_scr_key_valid = dut.rvfi_ext_ic_scr_key_valid; + assign rvfi_if.ext_irq_valid = dut.rvfi_ext_irq_valid; + assign rvfi_if.ext_expanded_insn_valid = dut.rvfi_ext_expanded_insn_valid; + assign rvfi_if.ext_expanded_insn = dut.rvfi_ext_expanded_insn; + assign rvfi_if.ext_expanded_insn_last = dut.rvfi_ext_expanded_insn_last; // Irq interface connections assign irq_vif.reset = ~rst_n; // Dut_if interface connections diff --git a/dv/verilator/simple_system_cosim/ibex_simple_system_cosim_checker.sv b/dv/verilator/simple_system_cosim/ibex_simple_system_cosim_checker.sv index 3d97417970..71317fd38a 100644 --- a/dv/verilator/simple_system_cosim/ibex_simple_system_cosim_checker.sv +++ b/dv/verilator/simple_system_cosim/ibex_simple_system_cosim_checker.sv @@ -63,7 +63,8 @@ module ibex_simple_system_cosim_checker #( if (riscv_cosim_step(cosim_handle, u_top.rvfi_rd_addr, u_top.rvfi_rd_wdata, u_top.rvfi_pc_rdata, u_top.rvfi_trap, - u_top.rvfi_ext_rf_wr_suppress) == 0) + u_top.rvfi_ext_rf_wr_suppress, u_top.rvfi_ext_expanded_insn_valid, + u_top.rvfi_ext_expanded_insn, u_top.rvfi_ext_expanded_insn_last) == 0) begin $display("FAILURE: Co-simulation mismatch at time %t", $time()); for (int i = 0;i < riscv_cosim_get_num_errors(cosim_handle); ++i) begin From 8ae5e0fd8a1686a70077c4ec64b1ec6703580558 Mon Sep 17 00:00:00 2001 From: Samuel Riedel Date: Thu, 9 Oct 2025 23:44:56 +0000 Subject: [PATCH 04/10] [dv] Support expanded instructions in cosim Add a function and state to process multiple Ibex instructions that are modeled as a single instruction in riscv-isa-sim. The current function checks that the same registers are written overall. --- dv/cosim/spike_cosim.cc | 106 ++++++++++++++++++++++++++++++++++++++-- dv/cosim/spike_cosim.h | 8 +++ 2 files changed, 110 insertions(+), 4 deletions(-) diff --git a/dv/cosim/spike_cosim.cc b/dv/cosim/spike_cosim.cc index 1ac20483b7..27268c8a85 100644 --- a/dv/cosim/spike_cosim.cc +++ b/dv/cosim/spike_cosim.cc @@ -39,7 +39,10 @@ SpikeCosim::SpikeCosim(const std::string &isa_string, uint32_t start_pc, uint32_t pmp_num_regions, uint32_t pmp_granularity, uint32_t mhpm_counter_num, uint32_t dm_start_addr, uint32_t dm_end_addr) - : nmi_mode(false), pending_iside_error(false), insn_cnt(0) { + : nmi_mode(false), + pending_iside_error(false), + insn_cnt(0), + pending_expanded_insn(0) { FILE *log_file = nullptr; if (trace_log_path.length() != 0) { log = std::make_unique(trace_log_path.c_str()); @@ -216,7 +219,13 @@ bool SpikeCosim::step(uint32_t write_reg, uint32_t write_reg_data, uint32_t pc, // (If the current step causes a synchronous trap, it will be // recorded against the current pc) initial_spike_pc = (processor->get_state()->pc & 0xffffffff); - processor->step(1); + // Only step Spike if the current instruction is not an expanded one. Spike + // does not expand instructions, so we have to consume a single Spike step + // over multiple steps of Ibex. In the last Ibex step of the expanded + // instruction, we step Spike. + if (!expanded_insn_valid) { + processor->step(1); + } // ISS // - If encountered an async trap, @@ -311,8 +320,16 @@ bool SpikeCosim::step(uint32_t write_reg, uint32_t write_reg_data, uint32_t pc, suppressed_write_reg_data); } - if (!check_retired_instr(write_reg, write_reg_data, pc, suppress_reg_write)) { - return false; + if (expanded_insn_valid) { + if (!check_expanded_instr(write_reg, write_reg_data, pc, suppress_reg_write, + expanded_insn, expanded_insn_last)) { + return false; + } + } else { + if (!check_retired_instr(write_reg, write_reg_data, pc, + suppress_reg_write)) { + return false; + } } // Only increment insn_cnt and return true if there are no errors @@ -320,6 +337,82 @@ bool SpikeCosim::step(uint32_t write_reg, uint32_t write_reg_data, uint32_t pc, return true; } +bool SpikeCosim::check_expanded_instr(uint32_t write_reg, + uint32_t write_reg_data, uint32_t dut_pc, + bool suppress_reg_write, + uint32_t expanded_insn) { + // If this is the first step of an expanded instruction, set up our + // expectations. + if (!pending_expanded_insn) { + pending_expanded_insn = expanded_insn; + expanded_insn_pc = dut_pc; + expanded_reg_changes.clear(); + + // The ISS has just stepped once for the entire expanded instruction. + // We now collect all the GPR changes it produced and store them in a map + auto ®_changes = processor->get_state()->log_reg_write; + for (auto reg_change : reg_changes) { + // Check if it's a GPR write (type 0) + if ((reg_change.first & 0xf) == 0) { + uint32_t iss_write_reg = (reg_change.first >> 4) & 0x1f; + // Ignore writes to x0 + if (iss_write_reg == 0) { + continue; + } + uint32_t iss_write_data = reg_change.second.v[0]; + expanded_reg_changes[iss_write_reg] = iss_write_data; + } + } + } + + // TODO: We have to deal with suppressed writes here too + if (suppress_reg_write) { + return true; + } + + // If the DUT did not write a register, this is a valid micro-op (e.g., a + // memory access or intermediate calculation). There is nothing to check. + if (write_reg == 0) { + return true; + } + + // The DUT wrote a register. Check if it matches one of our expectations. + auto reg_change = expanded_reg_changes.find(write_reg); + + // DUT wrote, but ISS didn't + if (reg_change == expanded_reg_changes.end()) { + std::stringstream err_str; + err_str << "Expanded instruction at PC 0x" << std::hex << expanded_insn_pc + << ": DUT wrote to x" << std::dec << write_reg + << ", which was not an expected register write for this sequence."; + errors.emplace_back(err_str.str()); + return false; + } + + // The register index matches. Now check if the data matches. + uint32_t expected_data = reg_change->second; + if (write_reg_data != expected_data) { + std::stringstream err_str; + err_str << "Expanded instruction at PC 0x" << std::hex << expanded_insn_pc + << ": Data mismatch for register x" << std::dec << write_reg + << ". DUT wrote: 0x" << std::hex << write_reg_data + << ", but ISS expected: 0x" << expected_data; + errors.emplace_back(err_str.str()); + return false; + } + + // Match is perfect. Remove this from our set of expectations. + expanded_reg_changes.erase(reg_change); + + if (expanded_reg_changes.empty()) { + // All expected register writes have been matched. Clear the pending state. + pending_expanded_insn = 0; + expanded_insn_pc = 0; + } + + return true; +} + bool SpikeCosim::check_retired_instr(uint32_t write_reg, uint32_t write_reg_data, uint32_t dut_pc, bool suppress_reg_write) { @@ -335,6 +428,11 @@ bool SpikeCosim::check_retired_instr(uint32_t write_reg, << " , but the ISS retired: " << std::hex << (processor->get_state()->last_inst_pc & 0xffffffff); errors.emplace_back(err_str.str()); + if (pending_expanded_insn) { + err_str << " (while processing expanded instruction at PC 0x" << std::hex + << expanded_insn_pc << ")"; + errors.emplace_back(err_str.str()); + } return false; } diff --git a/dv/cosim/spike_cosim.h b/dv/cosim/spike_cosim.h index abbbff235e..0661e2cc6d 100644 --- a/dv/cosim/spike_cosim.h +++ b/dv/cosim/spike_cosim.h @@ -99,6 +99,11 @@ class SpikeCosim : public simif_t, public Cosim { unsigned int insn_cnt; + // Handle expanded insn + uint32_t pending_expanded_insn; + uint32_t expanded_insn_pc; + std::map expanded_reg_changes; + public: SpikeCosim(const std::string &isa_string, uint32_t start_pc, uint32_t start_mtvec, const std::string &trace_log_path, @@ -125,6 +130,9 @@ class SpikeCosim : public simif_t, public Cosim { bool check_retired_instr(uint32_t write_reg, uint32_t write_reg_data, uint32_t dut_pc, bool suppress_reg_write); + bool check_expanded_instr(uint32_t write_reg, uint32_t write_reg_data, + uint32_t dut_pc, bool suppress_reg_write, + uint32_t expanded_insn, bool expanded_insn_last); bool check_sync_trap(uint32_t write_reg, uint32_t pc, uint32_t initial_spike_pc); void set_mip(uint32_t pre_mip, uint32_t post_mip) override; From 274a178eba9d057fdf2cab87e477c7cb97f5d6a1 Mon Sep 17 00:00:00 2001 From: Samuel Riedel Date: Fri, 7 Nov 2025 10:06:38 +0000 Subject: [PATCH 05/10] [dv] Add debug logging to cosim --- dv/cosim/cosim.h | 6 +++++ dv/cosim/cosim_dpi.cc | 22 +++++++++++++++++++ dv/cosim/cosim_dpi.h | 3 +++ dv/cosim/cosim_dpi.svh | 3 +++ dv/cosim/spike_cosim.cc | 4 ++++ dv/cosim/spike_cosim.h | 3 +++ .../ibex_cosim_agent/ibex_cosim_scoreboard.sv | 16 ++++++++++++++ 7 files changed, 57 insertions(+) diff --git a/dv/cosim/cosim.h b/dv/cosim/cosim.h index 13e8920f47..c702b5cfce 100644 --- a/dv/cosim/cosim.h +++ b/dv/cosim/cosim.h @@ -164,6 +164,12 @@ class Cosim { // Clear internal vector of error descriptions virtual void clear_errors() = 0; + // Get a vector of strings describing dbg that have occurred during `step` + virtual const std::vector &get_dbg() = 0; + + // Clear internal vector of dbg descriptions + virtual void clear_dbg() = 0; + // Returns a count of instructions executed by co-simulator and DUT without // failures. virtual unsigned int get_insn_cnt() = 0; diff --git a/dv/cosim/cosim_dpi.cc b/dv/cosim/cosim_dpi.cc index f3466d489b..77d19e465c 100644 --- a/dv/cosim/cosim_dpi.cc +++ b/dv/cosim/cosim_dpi.cc @@ -118,6 +118,28 @@ void riscv_cosim_clear_errors(Cosim *cosim) { cosim->clear_errors(); } +int riscv_cosim_get_num_dbg(Cosim *cosim) { + assert(cosim); + + return cosim->get_dbg().size(); +} + +const char *riscv_cosim_get_dbg(Cosim *cosim, int index) { + assert(cosim); + + if (index >= cosim->get_dbg().size()) { + return nullptr; + } + + return cosim->get_dbg()[index].c_str(); +} + +void riscv_cosim_clear_dbg(Cosim *cosim) { + assert(cosim); + + cosim->clear_dbg(); +} + void riscv_cosim_write_mem_byte(Cosim *cosim, const svBitVecVal *addr, const svBitVecVal *d) { assert(cosim); diff --git a/dv/cosim/cosim_dpi.h b/dv/cosim/cosim_dpi.h index 9ead67385b..ff03176b4e 100644 --- a/dv/cosim/cosim_dpi.h +++ b/dv/cosim/cosim_dpi.h @@ -40,6 +40,9 @@ void riscv_cosim_set_iside_error(Cosim *cosim, svBitVecVal *addr); int riscv_cosim_get_num_errors(Cosim *cosim); const char *riscv_cosim_get_error(Cosim *cosim, int index); void riscv_cosim_clear_errors(Cosim *cosim); +int riscv_cosim_get_num_dbg(Cosim *cosim); +const char *riscv_cosim_get_dbg(Cosim *cosim, int index); +void riscv_cosim_clear_dbg(Cosim *cosim); void riscv_cosim_write_mem_byte(Cosim *cosim, const svBitVecVal *addr, const svBitVecVal *d); unsigned int riscv_cosim_get_insn_cnt(Cosim *cosim); diff --git a/dv/cosim/cosim_dpi.svh b/dv/cosim/cosim_dpi.svh index 33f8babe87..f1781f4a53 100644 --- a/dv/cosim/cosim_dpi.svh +++ b/dv/cosim/cosim_dpi.svh @@ -29,6 +29,9 @@ import "DPI-C" function int riscv_cosim_set_iside_error(chandle cosim_handle, bi import "DPI-C" function int riscv_cosim_get_num_errors(chandle cosim_handle); import "DPI-C" function string riscv_cosim_get_error(chandle cosim_handle, int index); import "DPI-C" function void riscv_cosim_clear_errors(chandle cosim_handle); +import "DPI-C" function int riscv_cosim_get_num_dbg(chandle cosim_handle); +import "DPI-C" function string riscv_cosim_get_dbg(chandle cosim_handle, int index); +import "DPI-C" function void riscv_cosim_clear_dbg(chandle cosim_handle); import "DPI-C" function void riscv_cosim_write_mem_byte(chandle cosim_handle, bit [31:0] addr, bit [7:0] d); import "DPI-C" function int unsigned riscv_cosim_get_insn_cnt(chandle cosim_handle); diff --git a/dv/cosim/spike_cosim.cc b/dv/cosim/spike_cosim.cc index 27268c8a85..b19e5a3268 100644 --- a/dv/cosim/spike_cosim.cc +++ b/dv/cosim/spike_cosim.cc @@ -881,6 +881,10 @@ const std::vector &SpikeCosim::get_errors() { return errors; } void SpikeCosim::clear_errors() { errors.clear(); } +const std::vector &SpikeCosim::get_dbg() { return dbg; } + +void SpikeCosim::clear_dbg() { dbg.clear(); } + void SpikeCosim::fixup_csr(int csr_num, uint32_t csr_val) { switch (csr_num) { case CSR_MSTATUS: { diff --git a/dv/cosim/spike_cosim.h b/dv/cosim/spike_cosim.h index 0661e2cc6d..726870a4bf 100644 --- a/dv/cosim/spike_cosim.h +++ b/dv/cosim/spike_cosim.h @@ -38,6 +38,7 @@ class SpikeCosim : public simif_t, public Cosim { bus_t bus; std::vector> mems; std::vector errors; + std::vector dbg; bool nmi_mode; typedef struct { @@ -151,6 +152,8 @@ class SpikeCosim : public simif_t, public Cosim { void set_iside_error(uint32_t addr) override; const std::vector &get_errors() override; void clear_errors() override; + const std::vector &get_dbg() override; + void clear_dbg() override; unsigned int get_insn_cnt() override; }; diff --git a/dv/uvm/core_ibex/common/ibex_cosim_agent/ibex_cosim_scoreboard.sv b/dv/uvm/core_ibex/common/ibex_cosim_agent/ibex_cosim_scoreboard.sv index deee913ca4..76d3ad2f61 100644 --- a/dv/uvm/core_ibex/common/ibex_cosim_agent/ibex_cosim_scoreboard.sv +++ b/dv/uvm/core_ibex/common/ibex_cosim_agent/ibex_cosim_scoreboard.sv @@ -166,6 +166,8 @@ class ibex_cosim_scoreboard extends uvm_scoreboard; rvfi_instr.trap, rvfi_instr.rf_wr_suppress, rvfi_instr.expanded_insn_valid, rvfi_instr.expanded_insn, rvfi_instr.expanded_insn_last)) begin + // Print the debug before the error to help with diagnosis + `uvm_info(`gfn, get_cosim_dbg_str(rvfi_instr.pc), UVM_LOW) // cosim instruction step doesn't match rvfi captured instruction, report a fatal error // with the details if (cfg.relax_cosim_check) begin @@ -173,6 +175,10 @@ class ibex_cosim_scoreboard extends uvm_scoreboard; end else begin `uvm_fatal(`gfn, get_cosim_error_str()) end + end else begin + if (riscv_cosim_get_num_dbg(cosim_handle) > 0) begin + `uvm_info(`gfn, get_cosim_dbg_str(rvfi_instr.pc), UVM_LOW) + end end end endtask: run_cosim_rvfi @@ -342,6 +348,16 @@ class ibex_cosim_scoreboard extends uvm_scoreboard; return error; endfunction : get_cosim_error_str + function string get_cosim_dbg_str(bit [31:0] pc); + string dbg = $sformatf("Cosim debug (DUT PC 0x%08x):\n", pc); + for (int i = 0; i < riscv_cosim_get_num_dbg(cosim_handle); ++i) begin + dbg = {dbg, riscv_cosim_get_dbg(cosim_handle, i), "\n"}; + end + riscv_cosim_clear_dbg(cosim_handle); + + return dbg; + endfunction : get_cosim_dbg_str + function void final_phase(uvm_phase phase); super.final_phase(phase); From e45add044f1a91e35fc211d75249f592173502ba Mon Sep 17 00:00:00 2001 From: Samuel Riedel Date: Fri, 7 Nov 2025 14:14:02 +0000 Subject: [PATCH 06/10] [dv] Track expanded instr writes to compare with Spike at the last instr Instead of stepping Spike once at the beginning of the expanded instruction and then comparing each next operation with Spike's changes, this commit logs all of Ibex's changes and then steps spike at the last expanded instruction, i.e., the committing instruction. This then allows checking a list of changes from Ibex with a list of changes from Spike. The reason to do it this way around is that the memory access checks are triggered by Spike. Therefore, when Spike steps through the expanded instruction, it simultaneously checks all memory accesses. Previously, only the first instructions on Ibex were executed at this stage and only those memory accesses were visible. Now, Ibex will also have completed the entire instruction and memory accesses, and the check initiated by Spike will succeed. --- dv/cosim/spike_cosim.cc | 194 +++++++++++++++++++++++++++------------- dv/cosim/spike_cosim.h | 10 ++- 2 files changed, 141 insertions(+), 63 deletions(-) diff --git a/dv/cosim/spike_cosim.cc b/dv/cosim/spike_cosim.cc index b19e5a3268..b2f1ff3f12 100644 --- a/dv/cosim/spike_cosim.cc +++ b/dv/cosim/spike_cosim.cc @@ -219,11 +219,14 @@ bool SpikeCosim::step(uint32_t write_reg, uint32_t write_reg_data, uint32_t pc, // (If the current step causes a synchronous trap, it will be // recorded against the current pc) initial_spike_pc = (processor->get_state()->pc & 0xffffffff); - // Only step Spike if the current instruction is not an expanded one. Spike - // does not expand instructions, so we have to consume a single Spike step - // over multiple steps of Ibex. In the last Ibex step of the expanded - // instruction, we step Spike. - if (!expanded_insn_valid) { + // Only step Spike if the current instruction is not an expanded one or the + // final operation of an expanded sequence. Spike does not expand + // instructions, so we have to consume a single Spike step over multiple steps + // of Ibex. Since Spike will initiate the memory interface checks, we must + // complete all the steps of the expanded instruction in Ibex before stepping + // Spike. That is why we step Spike on expanded_insn_last and then check that + // all modifications match. + if (!expanded_insn_valid || expanded_insn_last || sync_trap) { processor->step(1); } @@ -340,77 +343,146 @@ bool SpikeCosim::step(uint32_t write_reg, uint32_t write_reg_data, uint32_t pc, bool SpikeCosim::check_expanded_instr(uint32_t write_reg, uint32_t write_reg_data, uint32_t dut_pc, bool suppress_reg_write, - uint32_t expanded_insn) { - // If this is the first step of an expanded instruction, set up our - // expectations. + uint32_t expanded_insn, + bool expanded_insn_last) { + // Expanded instruction must be set when calling this function + assert(expanded_insn != 0); + + // If it's the first instruction of an expanded sequence, set pending state + // and save the PC. if (!pending_expanded_insn) { + if (expanded_insn_last) { + // This is the first and last instruction. This should never happen with + // the currently supported expanded instructions + std::stringstream err_str; + err_str << "Error: PC 0x" << std::hex << dut_pc + << ": First and last expanded instruction set simultaneously. " + "This should never happen."; + errors.emplace_back(err_str.str()); + return false; + } pending_expanded_insn = expanded_insn; expanded_insn_pc = dut_pc; - expanded_reg_changes.clear(); - - // The ISS has just stepped once for the entire expanded instruction. - // We now collect all the GPR changes it produced and store them in a map - auto ®_changes = processor->get_state()->log_reg_write; - for (auto reg_change : reg_changes) { - // Check if it's a GPR write (type 0) - if ((reg_change.first & 0xf) == 0) { - uint32_t iss_write_reg = (reg_change.first >> 4) & 0x1f; - // Ignore writes to x0 - if (iss_write_reg == 0) { - continue; - } - uint32_t iss_write_data = reg_change.second.v[0]; - expanded_reg_changes[iss_write_reg] = iss_write_data; - } - } - } - - // TODO: We have to deal with suppressed writes here too - if (suppress_reg_write) { - return true; } - // If the DUT did not write a register, this is a valid micro-op (e.g., a - // memory access or intermediate calculation). There is nothing to check. - if (write_reg == 0) { - return true; - } - - // The DUT wrote a register. Check if it matches one of our expectations. - auto reg_change = expanded_reg_changes.find(write_reg); - - // DUT wrote, but ISS didn't - if (reg_change == expanded_reg_changes.end()) { + // Verify that the PC didn't change + if (dut_pc != expanded_insn_pc) { std::stringstream err_str; - err_str << "Expanded instruction at PC 0x" << std::hex << expanded_insn_pc - << ": DUT wrote to x" << std::dec << write_reg - << ", which was not an expected register write for this sequence."; + err_str << "Error: PC 0x" << std::hex << dut_pc + << ": PC changed during expanded instruction. Expected 0x" + << std::hex << expanded_insn_pc; errors.emplace_back(err_str.str()); + // This is a fatal error, reset state + pending_expanded_insn = 0; + dut_reg_changes.clear(); return false; } - // The register index matches. Now check if the data matches. - uint32_t expected_data = reg_change->second; - if (write_reg_data != expected_data) { - std::stringstream err_str; - err_str << "Expanded instruction at PC 0x" << std::hex << expanded_insn_pc - << ": Data mismatch for register x" << std::dec << write_reg - << ". DUT wrote: 0x" << std::hex << write_reg_data - << ", but ISS expected: 0x" << expected_data; - errors.emplace_back(err_str.str()); - return false; + // Push the DUT's register write to the queue for later unless its write to x0 + if (write_reg != 0 && !suppress_reg_write) { + dut_reg_changes[write_reg] = write_reg_data; } - // Match is perfect. Remove this from our set of expectations. - expanded_reg_changes.erase(reg_change); + if (!expanded_insn_last) { + // This is part of an expanded instruction, but not the last part. + // We just store the state and return true, as we wait for the final + // instruction. The ISS did NOT step yet, so we cannot compare the states + // yet. + return true; + } else { + // This is the final instruction of this sequence. Time to check all + // buffered writes. + + // Check ISS PC vs our saved PC + if ((processor->get_state()->last_inst_pc & 0xffffffff) != + expanded_insn_pc) { + std::stringstream err_str; + err_str << "PC mismatch, DUT retired expanded instruction at: " + << std::hex << expanded_insn_pc + << " , but the ISS retired: " << std::hex + << (processor->get_state()->last_inst_pc & 0xffffffff); + errors.emplace_back(err_str.str()); + pending_expanded_insn = 0; + dut_reg_changes.clear(); + return false; + } + + // Get all ISS changes and a working copy of DUT writes + auto &iss_reg_changes = processor->get_state()->log_reg_write; + bool all_checks_pass = true; - if (expanded_reg_changes.empty()) { - // All expected register writes have been matched. Clear the pending state. + // For each ISS change, find a matching DUT write. + for (auto iss_reg_change : iss_reg_changes) { + // reg_change.first provides register type in bottom 4 bits, then register + // index above that + // Ignore writes to x0 + if (iss_reg_change.first == 0) + continue; + + // register is GPR + if ((iss_reg_change.first & 0xf) == 0) { + // Find match by register address first + uint32_t iss_write_reg = (iss_reg_change.first >> 4) & 0x1f; + auto dut_reg_change = dut_reg_changes.find(iss_write_reg); + // Check if found + if (dut_reg_change != dut_reg_changes.end()) { + // Perform the regular write check + // dut_reg_change->first is write_reg, dut_reg_change->second is + // write_reg_data + if (!check_gpr_write(iss_reg_change, dut_reg_change->first, + dut_reg_change->second)) { + // Data mismatch --> continue to capture mutliple mismatches but + // eventually fail + all_checks_pass = false; + } + // Consume this write from the map + dut_reg_changes.erase(dut_reg_change); + } else { + // No DUT write found for this ISS register. + std::stringstream err_str; + uint32_t cosim_write_reg_data = iss_reg_change.second.v[0]; + err_str << "PC 0x" << std::hex << expanded_insn_pc + << ": ISS wrote GPR x" << iss_write_reg << " (val 0x" + << std::hex << cosim_write_reg_data + << "), but no matching write was found from the DUT's " + "expanded instructions."; + errors.emplace_back(err_str.str()); + all_checks_pass = false; + } + } else if ((iss_reg_change.first & 0xf) == 4) { + // register is CSR + on_csr_write(iss_reg_change); + } else { + // should never see other types + assert(false); + } + } + + // Make sure there are no DUT writes left over + if (!dut_reg_changes.empty()) { + for (auto &extra_write : dut_reg_changes) { + std::stringstream err_str; + err_str << "PC 0x" << std::hex << expanded_insn_pc + << ": DUT expanded instruction wrote GPR x" << extra_write.first + << " with value 0x" << std::hex << extra_write.second + << ", but this write was not expected by the ISS."; + errors.emplace_back(err_str.str()); + all_checks_pass = false; + } + } + + // This was the last instruction of this expanded instruction. Cleanup state + // and return pending_expanded_insn = 0; - expanded_insn_pc = 0; - } + dut_reg_changes.clear(); - return true; + // Final check for any errors added during the process + if (errors.size() != 0) { + all_checks_pass = false; + } + + return all_checks_pass; + } } bool SpikeCosim::check_retired_instr(uint32_t write_reg, diff --git a/dv/cosim/spike_cosim.h b/dv/cosim/spike_cosim.h index 726870a4bf..ef238d021e 100644 --- a/dv/cosim/spike_cosim.h +++ b/dv/cosim/spike_cosim.h @@ -100,10 +100,16 @@ class SpikeCosim : public simif_t, public Cosim { unsigned int insn_cnt; - // Handle expanded insn + // Handle expanded instructions + // Set to one once we start processing an expanded instruction. Used to track + // the start of a new expanded instruction. uint32_t pending_expanded_insn; + // Keep track of the PC of an expanded instruction to ensure it doesn't change + // without completing the expansion and getting a `expanded_insn_last` signal. uint32_t expanded_insn_pc; - std::map expanded_reg_changes; + // Keep track of register writes during an expanded instruction to compare + // with the ISS's register changes in the `expanded_insn_last` step. + std::map dut_reg_changes; public: SpikeCosim(const std::string &isa_string, uint32_t start_pc, From 9060bf9092dc848d77306c0335c08dc45d7b1328 Mon Sep 17 00:00:00 2001 From: Samuel Riedel Date: Mon, 15 Dec 2025 15:57:10 +0000 Subject: [PATCH 07/10] [dv] Add Zcmp instruction tests to testlist.yml --- .../riscv_dv_extension/testlist.yaml | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/dv/uvm/core_ibex/riscv_dv_extension/testlist.yaml b/dv/uvm/core_ibex/riscv_dv_extension/testlist.yaml index cff5bf0e4d..5541640f6b 100644 --- a/dv/uvm/core_ibex/riscv_dv_extension/testlist.yaml +++ b/dv/uvm/core_ibex/riscv_dv_extension/testlist.yaml @@ -1166,3 +1166,27 @@ gen_opts: > +enable_zcb_extension=1 rtl_test: core_ibex_base_test + +- test: riscv_zcmp_balanced_test + desc: > + Random instruction test with zcmp instructions in balanced configuration + iterations: 10 + gen_test: riscv_rand_instr_test + gen_opts: > + +enable_zcb_extension=1 + +enable_zcmp_extension=1 + rtl_test: core_ibex_base_test + +- test: riscv_zcmp_directed_test + desc: > + Random instruction test with zcmp instructions in balanced configuration + iterations: 10 + gen_test: riscv_instr_base_test + gen_opts: > + +enable_zcb_extension=1 + +enable_zcmp_extension=1 + +directed_instr_0=riscv_zcmp_chain_instr_stream,1 + +instr_cnt=100 + +num_of_sub_program=0 + +no_branch_jump=1 + rtl_test: core_ibex_base_test From 6a16d3f8796e6d5b00b9024d62afed9f60e1a352 Mon Sep 17 00:00:00 2001 From: Samuel Riedel Date: Mon, 15 Dec 2025 16:07:02 +0000 Subject: [PATCH 08/10] WIP: Update riscv-dv Clean up once our changes are upstreamed --- vendor/google_riscv-dv.vendor.hjson | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vendor/google_riscv-dv.vendor.hjson b/vendor/google_riscv-dv.vendor.hjson index ea249598b3..972d9eb5f3 100644 --- a/vendor/google_riscv-dv.vendor.hjson +++ b/vendor/google_riscv-dv.vendor.hjson @@ -6,8 +6,8 @@ target_dir: "google_riscv-dv", upstream: { - url: "https://github.com/chipsalliance/riscv-dv", - rev: "master", + url: "https://github.com/SamuelRiedel/riscv-dv", + rev: "zcmp-extension", }, exclude_from_upstream: [ From acfcec0f3be9103f9d9f59fe72d6a92725c00ff7 Mon Sep 17 00:00:00 2001 From: Samuel Riedel Date: Mon, 15 Dec 2025 16:07:06 +0000 Subject: [PATCH 09/10] Update google_riscv-dv to SamuelRiedel/riscv-dv@71ceff7 Update code from upstream repository https://github.com/SamuelRiedel/riscv-dv to revision 71ceff7bb7d332fcf8037752ccdec19b33c2d210 Signed-off-by: Samuel Riedel --- vendor/google_riscv-dv.lock.hjson | 6 +- .../.github/workflows/build-spike.yml | 2 +- .../.github/workflows/run-tests.yml | 27 +- vendor/google_riscv-dv/__init__.py | 0 vendor/google_riscv-dv/run.py | 25 +- .../scripts/instr_trace_compare.py | 2 + vendor/google_riscv-dv/scripts/lib.py | 18 +- .../scripts/renode_log_to_trace_csv.py | 4 + .../google_riscv-dv/scripts/renode_wrapper.py | 49 ++- .../src/isa/riscv_compressed_instr.sv | 17 +- vendor/google_riscv-dv/src/isa/riscv_instr.sv | 46 ++- .../src/isa/riscv_instr_cov.svh | 32 ++ .../src/isa/riscv_zcb_instr.sv | 225 ++++++++++++++ .../src/isa/riscv_zcmp_instr.sv | 225 ++++++++++++++ .../google_riscv-dv/src/isa/rv32zcb_instr.sv | 29 ++ .../google_riscv-dv/src/isa/rv32zcmp_instr.sv | 24 ++ .../google_riscv-dv/src/isa/rv64zcb_instr.sv | 18 ++ .../src/riscv_asm_program_gen.sv | 1 + vendor/google_riscv-dv/src/riscv_defines.svh | 10 + .../src/riscv_directed_instr_lib.sv | 3 +- .../src/riscv_illegal_instr.sv | 47 +++ .../src/riscv_instr_cover_group.sv | 126 +++++++- .../src/riscv_instr_gen_config.sv | 16 + vendor/google_riscv-dv/src/riscv_instr_pkg.sv | 46 ++- .../google_riscv-dv/src/riscv_instr_stream.sv | 72 +++-- .../src/riscv_load_store_instr_lib.sv | 282 +++++++++++++++++- .../google_riscv-dv/src/riscv_loop_instr.sv | 8 +- .../test/riscv_instr_cov_test.sv | 4 +- vendor/google_riscv-dv/yaml/iss.yaml | 6 +- vendor/google_riscv-dv/yaml/whisper.json | 7 + 30 files changed, 1313 insertions(+), 64 deletions(-) create mode 100644 vendor/google_riscv-dv/__init__.py create mode 100644 vendor/google_riscv-dv/src/isa/riscv_zcb_instr.sv create mode 100644 vendor/google_riscv-dv/src/isa/riscv_zcmp_instr.sv create mode 100644 vendor/google_riscv-dv/src/isa/rv32zcb_instr.sv create mode 100644 vendor/google_riscv-dv/src/isa/rv32zcmp_instr.sv create mode 100644 vendor/google_riscv-dv/src/isa/rv64zcb_instr.sv create mode 100644 vendor/google_riscv-dv/yaml/whisper.json diff --git a/vendor/google_riscv-dv.lock.hjson b/vendor/google_riscv-dv.lock.hjson index b7d8d399a4..1d81f97e7d 100644 --- a/vendor/google_riscv-dv.lock.hjson +++ b/vendor/google_riscv-dv.lock.hjson @@ -1,4 +1,4 @@ -// Copyright lowRISC contributors. +// Copyright lowRISC contributors (OpenTitan project). // Licensed under the Apache License, Version 2.0, see LICENSE for details. // SPDX-License-Identifier: Apache-2.0 @@ -8,7 +8,7 @@ { upstream: { - url: https://github.com/chipsalliance/riscv-dv - rev: 71666ebacd69266b1abb7cdbad5e1897ce5884e6 + url: https://github.com/SamuelRiedel/riscv-dv + rev: 71ceff7bb7d332fcf8037752ccdec19b33c2d210 } } diff --git a/vendor/google_riscv-dv/.github/workflows/build-spike.yml b/vendor/google_riscv-dv/.github/workflows/build-spike.yml index 442e825e82..59fbfffb34 100644 --- a/vendor/google_riscv-dv/.github/workflows/build-spike.yml +++ b/vendor/google_riscv-dv/.github/workflows/build-spike.yml @@ -36,7 +36,7 @@ jobs: key: ${{ env.cache_name }}_${{ env.cache_date }} restore-keys: ${{ env.cache_name }}_ - - name: Install prerequisities + - name: Install prerequisites if: ${{ steps.cache.outputs.cache-hit != 'true' }} run: | sudo apt -qqy update && sudo apt -qqy --no-install-recommends install \ diff --git a/vendor/google_riscv-dv/.github/workflows/run-tests.yml b/vendor/google_riscv-dv/.github/workflows/run-tests.yml index dd93d0cf63..7858a7065c 100644 --- a/vendor/google_riscv-dv/.github/workflows/run-tests.yml +++ b/vendor/google_riscv-dv/.github/workflows/run-tests.yml @@ -22,12 +22,14 @@ jobs: name: Prepare test types run: | python3 -m pip install pyyaml - echo "tests=$(python3 .github/scripts/parse_testlist.py $RISCV_TARGET)" | tee -a $GITHUB_OUTPUT + python3 .github/scripts/parse_testlist.py $RISCV_TARGET > tests.list + echo "tests=$(cat tests.list)" | tee -a $GITHUB_OUTPUT - id: hash name: Prepare files' hash run: | - echo "files-hash=$(sha256sum **/*.sv **/*.py **/*.yml **/*.yaml | cut -d\ -f1 | sha256sum | cut -d\ -f1)" | tee -a $GITHUB_OUTPUT - + sha256sum **/*.sv **/*.py **/*.yaml > file.hash + echo "files-hash=$(cat file.hash | cut -d\ -f1 | sha256sum | cut -d\ -f1)" | tee -a $GITHUB_OUTPUT + generate-code: runs-on: [ self-hosted, Linux, X64, gcp-custom-runners ] @@ -39,7 +41,7 @@ jobs: test: ${{ fromJSON(needs.generate-config.outputs.test-types) }} version: [ uvm ] include: - - test: riscv_arithmetic_basic_test + - test: riscv_arithmetic_basic_test version: pyflow env: GHA_EXTERNAL_DISK: additional-tools @@ -89,9 +91,10 @@ jobs: --isa $RISCV_TARGET --mabi ilp32 --steps gen -v -o test 2>&1 | tee test/generate.log - name: Upload Artifacts - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 if: always() with: + name: generate_code_${{ matrix.test }}_${{ matrix.version }} path: | test/asm_test/*.S @@ -116,7 +119,14 @@ jobs: - uses: actions/checkout@v4 - name: Install dependencies - run: sudo apt-get -qqy update && sudo apt-get -qqy install gcc-riscv64-unknown-elf device-tree-compiler + run: sudo apt-get -qqy update && sudo apt-get -qqy install device-tree-compiler + + - name: Install cross-compiler + shell: bash + run: | + echo "deb http://archive.ubuntu.com/ubuntu/ noble main universe" | sudo tee -a /etc/apt/sources.list > /dev/null + sudo apt -qqy update && sudo apt -qqy --no-install-recommends install gcc-riscv64-unknown-elf + riscv64-unknown-elf-gcc --version - name: Setup python # python dependencies cannot be properly downloaded with new versions of python @@ -174,9 +184,10 @@ jobs: --isa $RISCV_TARGET --mabi ilp32 --steps gcc_compile,iss_sim -v -o test 2>&1 | tee -a test/generate.log - name: Upload Artifacts - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 if: always() with: + name: run_tests_log_${{ matrix.test }}_${{ matrix.version }} path: | - test/asm_test/*.log test/*.log + test/**/${{ matrix.test }}*.log diff --git a/vendor/google_riscv-dv/__init__.py b/vendor/google_riscv-dv/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/vendor/google_riscv-dv/run.py b/vendor/google_riscv-dv/run.py index b6b8aa0a3b..8477c403ba 100644 --- a/vendor/google_riscv-dv/run.py +++ b/vendor/google_riscv-dv/run.py @@ -124,13 +124,14 @@ def get_generator_cmd(simulator, simulator_yaml, cov, exp, debug_cmd): sys.exit(RET_FAIL) -def parse_iss_yaml(iss, iss_yaml, isa, setting_dir, debug_cmd): +def parse_iss_yaml(iss, iss_yaml, isa, priv, setting_dir, debug_cmd): """Parse ISS YAML to get the simulation command Args: iss : target ISS used to look up in ISS YAML iss_yaml : ISS configuration file in YAML format isa : ISA variant passed to the ISS + priv: : privilege modes setting_dir : Generator setting directory debug_cmd : Produce the debug cmd log without running @@ -140,6 +141,9 @@ def parse_iss_yaml(iss, iss_yaml, isa, setting_dir, debug_cmd): logging.info("Processing ISS setup file : {}".format(iss_yaml)) yaml_data = read_yaml(iss_yaml) + # Path to the "yaml" subdirectory + yaml_dir = os.path.dirname(iss_yaml) + # Path to the "scripts" subdirectory my_path = os.path.dirname(os.path.realpath(__file__)) scripts_dir = os.path.join(my_path, "scripts") # Search for matched ISS @@ -166,7 +170,9 @@ def parse_iss_yaml(iss, iss_yaml, isa, setting_dir, debug_cmd): cmd = re.sub("\", variant, cmd) else: cmd = re.sub("\", isa, cmd) + cmd = re.sub("\", priv, cmd) cmd = re.sub("\", scripts_dir, cmd) + cmd = re.sub("\", yaml_dir, cmd) return cmd logging.error("Cannot find ISS {}".format(iss)) sys.exit(RET_FAIL) @@ -442,7 +448,12 @@ def gcc_compile(test_list, output_dir, isa, mabi, opts, debug_cmd): if 'gen_opts' in test: # Disable compressed instruction if re.search('disable_compressed_instr', test['gen_opts']): - test_isa = re.sub("c", "", test_isa) + # Note that this substitution assumes the cannonical order + # of extensions, i.e. that extensions with preceding + # underscores will be provided after all letter extensions. + # This assumption should hold true, as this is a + # requirement enforced by e.g. gcc + test_isa = re.sub(r"(rv.+?)c", r"\1", test_isa) # If march/mabi is not defined in the test gcc_opts, use the default # setting from the command line. if not re.search('march', cmd): @@ -641,7 +652,7 @@ def run_c_from_dir(c_test_dir, iss_yaml, isa, mabi, gcc_opts, iss, def iss_sim(test_list, output_dir, iss_list, iss_yaml, iss_opts, - isa, setting_dir, timeout_s, debug_cmd): + isa, priv, setting_dir, timeout_s, debug_cmd): """Run ISS simulation with the generated test program Args: @@ -651,13 +662,15 @@ def iss_sim(test_list, output_dir, iss_list, iss_yaml, iss_opts, iss_yaml : ISS configuration file in YAML format iss_opts : ISS command line options isa : ISA variant passed to the ISS + priv : privilege modes setting_dir : Generator setting directory timeout_s : Timeout limit in seconds debug_cmd : Produce the debug cmd log without running """ for iss in iss_list.split(","): log_dir = ("{}/{}_sim".format(output_dir, iss)) - base_cmd = parse_iss_yaml(iss, iss_yaml, isa, setting_dir, debug_cmd) + base_cmd = parse_iss_yaml(iss, iss_yaml, isa, priv, setting_dir, debug_cmd) + base_cmd += iss_opts logging.info("{} sim log dir: {}".format(iss, log_dir)) run_cmd_output(["mkdir", "-p", log_dir]) for test in test_list: @@ -814,6 +827,8 @@ def parse_args(cwd): command is not specified") parser.add_argument("--isa", type=str, default="", help="RISC-V ISA subset") + parser.add_argument("--priv", type=str, default="m", + help="RISC-V privilege modes enabled in simulation [su]") parser.add_argument("-m", "--mabi", type=str, default="", help="mabi used for compilation", dest="mabi") parser.add_argument("--gen_timeout", type=int, default=360, @@ -1147,7 +1162,7 @@ def main(): if args.steps == "all" or re.match(".*iss_sim.*", args.steps): iss_sim(matched_list, output_dir, args.iss, args.iss_yaml, args.iss_opts, - args.isa, args.core_setting_dir, args.iss_timeout, + args.isa, args.priv, args.core_setting_dir, args.iss_timeout, args.debug) # Compare ISS simulation result diff --git a/vendor/google_riscv-dv/scripts/instr_trace_compare.py b/vendor/google_riscv-dv/scripts/instr_trace_compare.py index b0fd3e88cc..615665381c 100644 --- a/vendor/google_riscv-dv/scripts/instr_trace_compare.py +++ b/vendor/google_riscv-dv/scripts/instr_trace_compare.py @@ -125,6 +125,8 @@ def compare_trace_csv(csv1, csv2, name1, name2, log, instr_trace_2[trace_2_index].gpr, gpr_val_2) if gpr_state_change_2 == 1: + fd.write("Mismatch[{}]:\n[{}] {} : {}\n".format( + mismatch_cnt, trace_1_index, name1,trace.get_trace_string())) fd.write("{} instructions left in trace {}\n".format( len(instr_trace_2) - trace_2_index, name2)) mismatch_cnt += len(instr_trace_2) - trace_2_index diff --git a/vendor/google_riscv-dv/scripts/lib.py b/vendor/google_riscv-dv/scripts/lib.py index 872752f001..a12cbc08be 100644 --- a/vendor/google_riscv-dv/scripts/lib.py +++ b/vendor/google_riscv-dv/scripts/lib.py @@ -88,7 +88,7 @@ def get_env_var(var, debug_cmd=None): return val -def run_cmd(cmd, timeout_s=999, exit_on_error=1, check_return_code=True, +def run_cmd(cmd, timeout_s=3600, exit_on_error=1, check_return_code=True, debug_cmd=None): """Run a command and return output @@ -103,6 +103,12 @@ def run_cmd(cmd, timeout_s=999, exit_on_error=1, check_return_code=True, debug_cmd.write(cmd) debug_cmd.write("\n\n") return + def killgroup(ps): + try: + os.killpg(os.getpgid(ps.pid), signal.SIGTERM) + except AttributeError: #killpg not available on windows + ps.kill() + sys.exit(130) try: ps = subprocess.Popen("exec " + cmd, shell=True, @@ -112,21 +118,17 @@ def run_cmd(cmd, timeout_s=999, exit_on_error=1, check_return_code=True, env=os.environ, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + output = ps.communicate(timeout = timeout_s)[0] except subprocess.CalledProcessError: logging.error(ps.communicate()[0]) sys.exit(RET_FAIL) except KeyboardInterrupt: logging.info("\nExited Ctrl-C from user request.") - sys.exit(130) - try: - output = ps.communicate(timeout=timeout_s)[0] + killgroup(ps) except subprocess.TimeoutExpired: logging.error("Timeout[{}s]: {}".format(timeout_s, cmd)) output = "" - try: - os.killpg(os.getpgid(ps.pid), signal.SIGTERM) - except AttributeError: #killpg not available on windows - ps.kill() + killgroup(ps) rc = ps.returncode if rc and check_return_code and rc > 0: logging.info(output) diff --git a/vendor/google_riscv-dv/scripts/renode_log_to_trace_csv.py b/vendor/google_riscv-dv/scripts/renode_log_to_trace_csv.py index cee4a29ac0..351cf29bda 100644 --- a/vendor/google_riscv-dv/scripts/renode_log_to_trace_csv.py +++ b/vendor/google_riscv-dv/scripts/renode_log_to_trace_csv.py @@ -78,6 +78,10 @@ def process_renode_sim_log(log_name, csv_name): if not line: continue + # We've hit ecall, quit + if line.startswith("ECALL:"): + break + # Skip non-regdump if not line.startswith("REGDUMP:"): continue diff --git a/vendor/google_riscv-dv/scripts/renode_wrapper.py b/vendor/google_riscv-dv/scripts/renode_wrapper.py index 245de92781..16c097f48a 100644 --- a/vendor/google_riscv-dv/scripts/renode_wrapper.py +++ b/vendor/google_riscv-dv/scripts/renode_wrapper.py @@ -8,12 +8,14 @@ REPL_TEMPLATE = """ memory: Memory.MappedMemory @ sysbus 0x80000000 - size: 0x10000 + size: {mem} -cpu: CPU.RiscV32 @ sysbus +cpu: CPU.{cpu_type} @ sysbus cpuType: "{isa}" timeProvider: clint hartId: 0 + {priv_levels} + {additional_cpu_parameters} clint: IRQControllers.CoreLevelInterruptor @ sysbus 0x02000000 [0,1] -> cpu@[3,7] @@ -29,8 +31,9 @@ cpu MaximumBlockSize 1 cpu SetHookAtBlockEnd "print('REGDUMP:' + ','.join(self.GetRegistersValues()))" +cpu InstallCustomInstructionHandlerFromString "00000000000000000000000001110011" "print('ECALL:');" -emulation RunFor "0.000100" +emulation RunFor "0.001" quit """ @@ -69,6 +72,32 @@ def main(): required=True, help="ELF file to run", ) + parser.add_argument( + "--mem-size", + type=str, + default="0x100000", + help="Memory size", + ) + parser.add_argument( + "--cpu-type", + type=str, + default="Riscv32", + help="Renode CPU type", + ) + parser.add_argument( + "--priv", + type=str, + default="", + help="Supported privilege levels", + ) + # Some CPUs might not expose these parameters as configurable + # allow the testing software to ignore/override them if needed + parser.add_argument( + "--additional-cpu-parameters", + type=str, + default="allowUnalignedAccesses: true", + help="Additional CPU parameters", + ) args = parser.parse_args() @@ -77,6 +106,16 @@ def main(): repl = os.path.join(tmpdir, "riscv.repl") resc = os.path.join(tmpdir, "riscv.resc") + priv_levels = "" + if args.priv: + priv_levels += "privilegeLevels: PrivilegeLevels." + if "m" in args.priv: + priv_levels += "Machine" + if "s" in args.priv: + priv_levels += "Supervisor" + if "u" in args.priv: + priv_levels += "User" + params = { "renode": args.renode, "isa": args.isa, @@ -84,6 +123,10 @@ def main(): "repl": repl, "resc": resc, "log": args.log, + "mem": args.mem_size, + "cpu_type": args.cpu_type, + "priv_levels": priv_levels, + "additional_cpu_parameters": args.additional_cpu_parameters, } # Render REPL template diff --git a/vendor/google_riscv-dv/src/isa/riscv_compressed_instr.sv b/vendor/google_riscv-dv/src/isa/riscv_compressed_instr.sv index 02fdf338a5..a603262fe7 100644 --- a/vendor/google_riscv-dv/src/isa/riscv_compressed_instr.sv +++ b/vendor/google_riscv-dv/src/isa/riscv_compressed_instr.sv @@ -33,12 +33,19 @@ class riscv_compressed_instr extends riscv_instr; } // C_ADDI16SP is only valid when rd == SP if (instr_name == C_ADDI16SP) { + rs1 == SP; rd == SP; } if (instr_name inside {C_JR, C_JALR}) { rs2 == ZERO; rs1 != ZERO; } + // These formats potentially have rd == rs1 + if (format inside {CA_FORMAT, CB_FORMAT, CI_FORMAT, CR_FORMAT}) { + if (has_rd && has_rs1) { + rd == rs1; + } + } } constraint imm_val_c { @@ -156,7 +163,13 @@ class riscv_compressed_instr extends riscv_instr; has_rs1 = 1'b0; has_imm = 1'b0; end - CI_FORMAT, CIW_FORMAT: begin + CI_FORMAT: begin + if (instr_name inside {C_LI, C_LUI, C_LWSP, C_NOP, C_EBREAK}) begin + has_rs1 = 1'b0; + end + has_rs2 = 1'b0; + end + CIW_FORMAT: begin has_rs1 = 1'b0; has_rs2 = 1'b0; end @@ -166,7 +179,7 @@ class riscv_compressed_instr extends riscv_instr; has_rd = 1'b0; end CB_FORMAT: begin - if (instr_name != C_ANDI) has_rd = 1'b0; + if (instr_name inside {C_BEQZ, C_BNEZ}) has_rd = 1'b0; has_rs2 = 1'b0; end endcase diff --git a/vendor/google_riscv-dv/src/isa/riscv_instr.sv b/vendor/google_riscv-dv/src/isa/riscv_instr.sv index f44e972a28..229afc9235 100644 --- a/vendor/google_riscv-dv/src/isa/riscv_instr.sv +++ b/vendor/google_riscv-dv/src/isa/riscv_instr.sv @@ -45,6 +45,8 @@ class riscv_instr extends uvm_object; rand riscv_reg_t rs1; rand riscv_reg_t rd; rand bit [31:0] imm; + rand bit [3:0] rlist; + rand int stack_adj; // Helper fields bit [31:0] imm_mask = 32'hFFFF_FFFF; @@ -66,6 +68,8 @@ class riscv_instr extends uvm_object; bit has_rs2 = 1'b1; bit has_rd = 1'b1; bit has_imm = 1'b1; + bit has_rlist = 1'b0; + bit has_stack_adj = 1'b0; constraint imm_c { if (instr_name inside {SLLIW, SRLIW, SRAIW}) { @@ -111,7 +115,8 @@ class riscv_instr extends uvm_object; if (cfg.no_fence && (instr_name inside {FENCE, FENCE_I, SFENCE_VMA})) continue; if ((instr_inst.group inside {supported_isa}) && !(cfg.disable_compressed_instr && - (instr_inst.group inside {RV32C, RV64C, RV32DC, RV32FC, RV128C})) && + (instr_inst.group inside {RV32C, RV64C, RV32DC, RV32FC, RV128C, + RV32ZCB, RV64ZCB, RV32ZCMP, RV64ZCMP})) && !(!cfg.enable_floating_point && (instr_inst.group inside {RV32F, RV64F, RV32D, RV64D})) && !(!cfg.enable_vector_extension && @@ -296,6 +301,7 @@ class riscv_instr extends uvm_object; rs2.rand_mode(has_rs2); rd.rand_mode(has_rd); imm.rand_mode(has_imm); + stack_adj.rand_mode(has_stack_adj); if (category != CSR) begin csr.rand_mode(0); end @@ -586,6 +592,40 @@ class riscv_instr extends uvm_object; return imm_str; endfunction + virtual function string get_rlist(); + // rlist 0-3 are reserved for future extensions + if (rlist < 4 || rlist > 15) begin + `uvm_fatal(`gfn, $sformatf("Unsupported rlist: %0d", rlist)) + end + // Handle the specific cases and use a default for the general pattern. + case(rlist) + 4: return "{ra}"; + 5: return "{ra, s0}"; + 15: return "{ra, s0-s11}"; // The special case for s10/s11 + // The default case handles the general pattern for rlist 6 through 14 + default: return $sformatf("{ra, s0-s%0d}", rlist - 5); + endcase + endfunction + + virtual function riscv_reglist_t get_rlist_as_list(); + case(rlist) + 4: return '{RA}; + 5: return '{RA, S0}; + 6: return '{RA, S0, S1}; + 7: return '{RA, S0, S1, S2}; + 8: return '{RA, S0, S1, S2, S3}; + 9: return '{RA, S0, S1, S2, S3, S4}; + 10: return '{RA, S0, S1, S2, S3, S4, S5}; + 11: return '{RA, S0, S1, S2, S3, S4, S5, S6}; + 12: return '{RA, S0, S1, S2, S3, S4, S5, S6, S7}; + 13: return '{RA, S0, S1, S2, S3, S4, S5, S6, S7, S8}; + 14: return '{RA, S0, S1, S2, S3, S4, S5, S6, S7, S8, S9}; + 15: return '{RA, S0, S1, S2, S3, S4, S5, S6, S7, S8, S9, S10, S11}; + default: `uvm_fatal(`gfn, $sformatf("Unsupported rlist: %0d", rlist)) + endcase + return '{}; + endfunction + virtual function void clear_unused_label(); if(has_label && !is_branch_target && is_local_numeric_label) begin has_label = 1'b0; @@ -620,6 +660,10 @@ class riscv_instr extends uvm_object; imm_str = $sformatf("%0d", $signed(imm)); endfunction + virtual function int get_imm_val(); + return $signed(imm); + endfunction + `include "isa/riscv_instr_cov.svh" endclass diff --git a/vendor/google_riscv-dv/src/isa/riscv_instr_cov.svh b/vendor/google_riscv-dv/src/isa/riscv_instr_cov.svh index 6819f3836c..54773de3d1 100644 --- a/vendor/google_riscv-dv/src/isa/riscv_instr_cov.svh +++ b/vendor/google_riscv-dv/src/isa/riscv_instr_cov.svh @@ -408,6 +408,38 @@ // c.j imm get_val(operands[0], imm); end + CU_FORMAT: begin + rs1 = get_gpr(operands[0]); + rs1_value = get_gpr_state(operands[0]); + end + CLB_FORMAT, CLH_FORMAT: begin + get_val(operands[2], imm); + rs1 = get_gpr(operands[1]); + rs1_value = get_gpr_state(operands[1]); + end + CSB_FORMAT, CSH_FORMAT: begin + rs2 = get_gpr(operands[0]); + rs2_value = get_gpr_state(operands[0]); + get_val(operands[2], imm); + rs1 = get_gpr(operands[1]); + rs1_value = get_gpr_state(operands[1]); + end + CMMV_FORMAT: begin + // cm.mva01s rs1, rs2 + // cm.mvsa01 r1s, r2s + rs1 = get_gpr(operands[0]); + rs1_value = get_gpr_state(operands[0]); + rs2 = get_gpr(operands[1]); + rs2_value = get_gpr_state(operands[1]); + end + CMPP_FORMAT: begin + // cm.push {reg_list}, -stack_adj + // cm.pop {reg_list}, stack_adj + // cm.popret {reg_list}, stack_adj + // cm.popretz {reg_list}, stack_adj + get_val(operands[0], rlist); + get_val(operands[1], stack_adj); + end default: `uvm_fatal(`gfn, $sformatf("Unsupported format %0s", format)) endcase endfunction : update_src_regs diff --git a/vendor/google_riscv-dv/src/isa/riscv_zcb_instr.sv b/vendor/google_riscv-dv/src/isa/riscv_zcb_instr.sv new file mode 100644 index 0000000000..7eb7ce4b69 --- /dev/null +++ b/vendor/google_riscv-dv/src/isa/riscv_zcb_instr.sv @@ -0,0 +1,225 @@ +/* + * Copyright 2020 Google LLC + * Copyright 2023 Frontgrade Gaisler + * + * 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. + */ + +class riscv_zcb_instr extends riscv_instr; + + // This code is based on the frozen v.1.0.1 code reduction specification. + // Most of the code is copied from the riscv_compressed_instr class. + + constraint rvc_csr_c { + // Registers specified by the three-bit rs1’, rs2’, and rd/rs1’ + if (format inside {CLB_FORMAT, CSB_FORMAT, CLH_FORMAT, CSH_FORMAT, CU_FORMAT, CA_FORMAT}) { + if (has_rs1) { + rs1 inside {[S0:A5]}; + } + if (has_rs2) { + rs2 inside {[S0:A5]}; + } + if (has_rd) { + rd inside {[S0:A5]}; + } + } + // CU_FORMAT and CA_FORMAT has rd == rs1 + if (format inside {CU_FORMAT, CA_FORMAT}) { + if (has_rd && has_rs1) { + rd == rs1; + } + } + } + + `uvm_object_utils(riscv_zcb_instr) + + function new(string name = ""); + super.new(name); + rs1 = S0; + rs2 = S0; + rd = S0; + is_compressed = 1'b1; + endfunction : new + + virtual function void set_imm_len(); + if (format inside {CLB_FORMAT, CSB_FORMAT}) begin + imm_len = 2; + end else if (format inside {CLH_FORMAT, CSH_FORMAT}) begin + imm_len = 1; + end + endfunction : set_imm_len + + virtual function void set_rand_mode(); + case (format) inside + CLB_FORMAT: begin + has_rs2 = 1'b0; + end + CSB_FORMAT: begin + has_rd = 1'b0; + end + CLH_FORMAT: begin + has_rs2 = 1'b0; + end + CSH_FORMAT: begin + has_rd = 1'b0; + end + CU_FORMAT: begin + has_rs2 = 1'b0; + has_imm = 1'b0; + end + CA_FORMAT: begin + has_imm = 1'b0; + end + default: `uvm_info(`gfn, $sformatf("Unsupported format %0s", format.name()), UVM_LOW) + endcase + endfunction + + // Convert the instruction to assembly code + virtual function string convert2asm(string prefix = ""); + string asm_str; + asm_str = format_string(get_instr_name(), MAX_INSTR_STR_LEN); + case(format) + CLB_FORMAT, CLH_FORMAT: + asm_str = $sformatf("%0s%0s, %0s(%0s)", asm_str, rd.name(), get_imm(), rs1.name()); + CSB_FORMAT, CSH_FORMAT: + asm_str = $sformatf("%0s%0s, %0s(%0s)", asm_str, rs2.name(), get_imm(), rs1.name()); + CU_FORMAT: + asm_str = $sformatf("%0s%0s", asm_str, rs1.name); + CA_FORMAT: + asm_str = $sformatf("%0s%0s, %0s", asm_str, rd.name(), rs2.name()); + default: `uvm_info(`gfn, $sformatf("Unsupported format %0s", format.name()), UVM_LOW) + endcase + + if (comment != "") + asm_str = {asm_str, " #",comment}; + return asm_str.tolower(); + endfunction : convert2asm + + // Convert the instruction to assembly code + virtual function string convert2bin(string prefix = ""); + string binary; + case (instr_name) inside + C_LBU: + binary = $sformatf("0x%4h", {get_func6(), get_c_gpr(rs1), imm[0], imm[1], + get_c_gpr(rd), get_c_opcode()}); + C_LHU: + binary = $sformatf("0x%4h", {get_func6(), get_c_gpr(rs1), 1'b0, imm[1], + get_c_gpr(rd), get_c_opcode()}); + C_LH: + binary = $sformatf("0x%4h", {get_func6(), get_c_gpr(rs1), 1'b1, imm[1], + get_c_gpr(rd), get_c_opcode()}); + C_SB: + binary = $sformatf("0x%4h", {get_func6(), get_c_gpr(rs1), imm[0], imm[1], + get_c_gpr(rs2), get_c_opcode()}); + C_SH: + binary = $sformatf("0x%4h", {get_func6(), get_c_gpr(rs1), 1'b0, imm[1], + get_c_gpr(rs2), get_c_opcode()}); + C_ZEXT_B: + binary = $sformatf("0x%4h", {get_func6(), get_c_gpr(rd), 5'b11000, + get_c_opcode()}); + C_SEXT_B: + binary = $sformatf("0x%4h", {get_func6(), get_c_gpr(rd), 5'b11001, + get_c_opcode()}); + C_ZEXT_H: + binary = $sformatf("0x%4h", {get_func6(), get_c_gpr(rd), 5'b11010, + get_c_opcode()}); + C_SEXT_H: + binary = $sformatf("0x%4h", {get_func6(), get_c_gpr(rd), 5'b11011, + get_c_opcode()}); + C_ZEXT_W: + binary = $sformatf("0x%4h", {get_func6(), get_c_gpr(rd), 5'b11100, + get_c_opcode()}); + C_NOT: + binary = $sformatf("0x%4h", {get_func6(), get_c_gpr(rd), 5'b11101, + get_c_opcode()}); + C_MUL: + binary = $sformatf("0x%4h", {get_func6(), get_c_gpr(rd), 2'b10, + get_c_gpr(rs2), get_c_opcode()}); + default: `uvm_fatal(`gfn, $sformatf("Unsupported instruction %0s", instr_name.name())) + endcase + return {prefix, binary}; + endfunction : convert2bin + + // Get opcode for zcb instruction + virtual function bit [1:0] get_c_opcode(); + case (instr_name) inside + C_ZEXT_B, C_SEXT_B, C_ZEXT_H, C_SEXT_H, + C_ZEXT_W, C_NOT, C_MUL : get_c_opcode = 2'b01; + C_LBU, C_LHU, C_LH, C_SB, C_SH : get_c_opcode = 2'b00; + default: `uvm_fatal(`gfn, $sformatf("Unsupported instruction %0s", instr_name.name())) + endcase + endfunction : get_c_opcode + + virtual function bit [5:0] get_func6(); + case (instr_name) + C_LBU: get_func6 = 6'b100000; + C_LHU: get_func6 = 6'b100001; + C_LH: get_func6 = 6'b100001; + C_SB: get_func6 = 6'b100010; + C_SH: get_func6 = 6'b100011; + C_ZEXT_B: get_func6 = 6'b100011; + C_SEXT_B: get_func6 = 6'b100111; + C_ZEXT_H: get_func6 = 6'b100111; + C_SEXT_H: get_func6 = 6'b100111; + C_ZEXT_W: get_func6 = 6'b100111; + C_NOT: get_func6 = 6'b100111; + C_MUL: get_func6 = 6'b100111; + default: `uvm_fatal(`gfn, $sformatf("Unsupported instruction %0s", instr_name.name())) + endcase + endfunction : get_func6 + + virtual function bit is_supported(riscv_instr_gen_config cfg); + return (cfg.enable_zcb_extension && + // RV32C, RV32Zbb, RV32Zba, M/Zmmul is prerequisites for this extension + (RV32ZBB inside {supported_isa}) && + (RV32C inside {supported_isa}) && + (RV32ZBA inside {supported_isa}) && + ((RV32M inside {supported_isa}) || (RV32ZMMUL inside {supported_isa})) && + ((RV32ZCB inside {supported_isa} || RV64ZCB inside {supported_isa})) && + instr_name inside { + C_ZEXT_B, C_SEXT_B, C_ZEXT_H, C_SEXT_H, C_ZEXT_W, + C_NOT, C_MUL, C_LBU, C_LHU, C_LH, C_SB, C_SH + }); + endfunction : is_supported + + // For coverage + virtual function void update_src_regs(string operands[$]); + case(format) + CU_FORMAT: begin + rs1 = get_gpr(operands[0]); + rs1_value = get_gpr_state(operands[0]); + end + CLB_FORMAT, CLH_FORMAT: begin + get_val(operands[2], imm); + rs1 = get_gpr(operands[1]); + rs1_value = get_gpr_state(operands[1]); + end + CSB_FORMAT, CSH_FORMAT: begin + rs2 = get_gpr(operands[0]); + rs2_value = get_gpr_state(operands[0]); + get_val(operands[2], imm); + rs1 = get_gpr(operands[1]); + rs1_value = get_gpr_state(operands[1]); + end + CA_FORMAT: begin + rs2 = get_gpr(operands[1]); + rs2_value = get_gpr_state(operands[1]); + rs1 = get_gpr(operands[0]); + rs1_value = get_gpr_state(operands[0]); + end + default: ; + endcase + super.update_src_regs(operands); + endfunction : update_src_regs + +endclass : riscv_zcb_instr; diff --git a/vendor/google_riscv-dv/src/isa/riscv_zcmp_instr.sv b/vendor/google_riscv-dv/src/isa/riscv_zcmp_instr.sv new file mode 100644 index 0000000000..81c0c6dc72 --- /dev/null +++ b/vendor/google_riscv-dv/src/isa/riscv_zcmp_instr.sv @@ -0,0 +1,225 @@ +/* + * Copyright 2025 Google LLC + * Copyright 2025 lowRISC CIC + * + * 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. + */ + +class riscv_zcmp_instr extends riscv_instr; + + constraint rvc_csr_c { + // Solve the stack adjustment first, so we can control the stack size + solve stack_adj before rlist; + if (format == CMPP_FORMAT) { + if (instr_name == CM_PUSH) { + // For PUSH, stack adjustment is negative + stack_adj inside {-16, -32, -48, -64, -80, -96, -112}; + (stack_adj == -16) -> rlist inside {[4:7]}; + (stack_adj == -32) -> rlist inside {[4:11]}; + (stack_adj == -48) -> rlist inside {[4:14]}; + (stack_adj == -64) -> rlist inside {[4:15]}; + (stack_adj == -80) -> rlist inside {[8:15]}; + (stack_adj == -96) -> rlist inside {[12:15]}; + (stack_adj == -112) -> rlist inside {15}; + } else { + // For POP and POPRET, stack adjustment is positive + stack_adj inside {16, 32, 48, 64, 80, 96, 112}; + (rlist inside {[4:7]}) -> stack_adj inside {16, 32, 48, 64}; + (rlist inside {[8:11]}) -> stack_adj inside {32, 48, 64, 80}; + (rlist inside {[12:14]}) -> stack_adj inside {48, 64, 80, 96}; + (rlist == 15) -> stack_adj inside {64, 80, 96, 112}; + } + // rlist can be anything between 4 and 15. 0-3 are reserved for future use. + rlist inside {[4:15]}; + } + if (format == CMMV_FORMAT) { + // Always has rs1 and rs2 and they must be different + rs1 != rs2; + // Those instructions use a special encoding, only S0, S1, S2-S7 are allowed + // which correspond to x8, x9, x18-x23, so the actual registers used are + // {r1sc[2:1]>0,r1sc[2:1]==0,r1sc[2:0]}; + // So for registers beyond x16 we prepend 0x10 to the three LSB + // For the registers x8 and x9 we prepend 0x01 to the three LSB + rs1 inside {S0, S1, [S2:S7]}; + rs2 inside {S0, S1, [S2:S7]}; + } + } + + `uvm_object_utils(riscv_zcmp_instr) + + function new(string name = ""); + super.new(name); + rs1 = S0; + rs2 = S0; + rlist = 4; + is_compressed = 1'b1; + stack_adj = instr_name == CM_PUSH ? -16 : 16; + endfunction : new + + virtual function void set_rand_mode(); + case (format) inside + CMMV_FORMAT : begin + has_imm = 1'b0; + end + CMPP_FORMAT : begin + has_rs1 = 1'b0; + has_rs2 = 1'b0; + has_rd = 1'b0; + has_rlist = 1'b1; + has_imm = 1'b0; + has_stack_adj = 1'b1; + end + default: `uvm_info(`gfn, $sformatf("Unsupported format %0s", format.name()), UVM_LOW) + endcase + endfunction + + // Convert the instruction to assembly code + virtual function string convert2asm(string prefix = ""); + string asm_str; + asm_str = format_string(get_instr_name(), MAX_INSTR_STR_LEN); + case(format) + CMMV_FORMAT: + asm_str = $sformatf("%0s %0s, %0s", asm_str, rs1.name(), rs2.name()); + CMPP_FORMAT: begin + asm_str = $sformatf("%0s %0s, %0d", asm_str, get_rlist(), get_stack_adj()); + end + default: `uvm_info(`gfn, $sformatf("Unsupported format %0s", format.name()), UVM_LOW) + endcase + + if (comment != "") + asm_str = {asm_str, " #",comment}; + return asm_str.tolower(); + endfunction : convert2asm + + // Convert the instruction to binary code + virtual function string convert2bin(string prefix = ""); + string binary; + case (instr_name) inside + CM_PUSH: + binary = $sformatf( + "0x%4h", {get_func6(), get_func2(), rlist, get_stack_adj_encoding(), get_c_opcode()}); + CM_POP: + binary = $sformatf( + "0x%4h", {get_func6(), get_func2(), rlist, get_stack_adj_encoding(), get_c_opcode()}); + CM_POPRETZ: + binary = $sformatf( + "0x%4h", {get_func6(), get_func2(), rlist, get_stack_adj_encoding(), get_c_opcode()}); + CM_POPRET: + binary = $sformatf( + "0x%4h", {get_func6(), get_func2(), rlist, get_stack_adj_encoding(), get_c_opcode()}); + CM_MVA01S: + binary = $sformatf( + "0x%4h", {get_func6(), get_c_gpr(rs1), get_func2(), get_c_gpr(rs2), get_c_opcode()}); + CM_MVSA01: + binary = $sformatf( + "0x%4h", {get_func6(), get_c_gpr(rs1), get_func2(), get_c_gpr(rs2), get_c_opcode()}); + default: `uvm_fatal(`gfn, $sformatf("Unsupported instruction %0s", instr_name.name())) + endcase + return {prefix, binary}; + endfunction : convert2bin + + // Get opcode for zcmp instruction + virtual function bit [1:0] get_c_opcode(); + case (instr_name) inside + CM_PUSH, CM_POP, CM_POPRETZ, CM_POPRET, CM_MVA01S, CM_MVSA01 : get_c_opcode = 2'b10; + default: `uvm_fatal(`gfn, $sformatf("Unsupported instruction %0s", instr_name.name())) + endcase + endfunction : get_c_opcode + + virtual function bit [5:0] get_func6(); + case (instr_name) inside + CM_PUSH: get_func6 = 6'b101110; + CM_POP: get_func6 = 6'b101110; + CM_POPRETZ: get_func6 = 6'b101111; + CM_POPRET: get_func6 = 6'b101111; + CM_MVSA01: get_func6 = 6'b101011; + CM_MVA01S: get_func6 = 6'b101011; + default: `uvm_fatal(`gfn, $sformatf("Unsupported instruction %0s", instr_name.name())) + endcase + endfunction : get_func6 + + virtual function bit [1:0] get_func2(); + case (instr_name) inside + CM_PUSH: get_func2 = 2'b00; + CM_POP: get_func2 = 2'b10; + CM_POPRETZ: get_func2 = 2'b00; + CM_POPRET: get_func2 = 2'b10; + CM_MVSA01: get_func2 = 2'b01; + CM_MVA01S: get_func2 = 2'b11; + default: `uvm_fatal(`gfn, $sformatf("Unsupported instruction %0s", instr_name.name())) + endcase + endfunction : get_func2 + + virtual function bit is_supported(riscv_instr_gen_config cfg); + `uvm_info(`gfn, "ZCMP Check supported", UVM_LOW) + return (cfg.enable_zcmp_extension && + // RV32C, RV32Zbb, RV32Zba, M/Zmmul is prerequisites for this extension + (RV32C inside {supported_isa} || RV64C inside {supported_isa}) && + (RV32ZCMP inside {supported_isa} || RV64ZCMP inside {supported_isa}) && + instr_name inside { + CM_PUSH, CM_POP, CM_POPRET, CM_POPRETZ, CM_MVA01S, CM_MVSA01 + }); + endfunction : is_supported + + // For coverage + virtual function void update_src_regs(string operands[$]); + case(format) + CMMV_FORMAT: begin + rs1 = get_gpr(operands[0]); + rs1_value = get_gpr_state(operands[0]); + rs2 = get_gpr(operands[1]); + rs2_value = get_gpr_state(operands[1]); + end + CMPP_FORMAT: begin + get_val(operands[0], rlist); + get_val(operands[1], stack_adj); + end + default: ; + endcase + super.update_src_regs(operands); + endfunction : update_src_regs + + virtual function bit [1:0] get_stack_adj_encoding(); + int unsigned stack_adj_base; + int unsigned stack_adj_abs; + bit [1:0] stack_adj_encoding; + if (XLEN == 32) begin + case (rlist) + 4, 5, 6, 7: stack_adj_base = 16; + 8, 9, 10, 11: stack_adj_base = 32; + 12, 13, 14: stack_adj_base = 48; + 15: stack_adj_base = 64; + default: stack_adj_base = 0; + endcase + end else begin + case (rlist) + 4, 5: stack_adj_base = 16; + 6, 7: stack_adj_base = 32; + 8, 9: stack_adj_base = 48; + 10, 11: stack_adj_base = 64; + 12, 13: stack_adj_base = 80; + 14: stack_adj_base = 96; + 15: stack_adj_base = 122; + default: stack_adj_base = 0; + endcase + end + stack_adj_abs = instr_name == CM_PUSH ? -stack_adj : stack_adj; + stack_adj_encoding = (stack_adj_abs - stack_adj_base) / 16; + return stack_adj_encoding; + endfunction + + virtual function int get_stack_adj(); + return stack_adj; + endfunction + +endclass : riscv_zcmp_instr; diff --git a/vendor/google_riscv-dv/src/isa/rv32zcb_instr.sv b/vendor/google_riscv-dv/src/isa/rv32zcb_instr.sv new file mode 100644 index 0000000000..d83fbd806f --- /dev/null +++ b/vendor/google_riscv-dv/src/isa/rv32zcb_instr.sv @@ -0,0 +1,29 @@ +/* + * Copyright 2020 Google LLC + * Copyright 2023 Frontgrade Gaisler + * + * 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. + */ + +`DEFINE_ZCB_INSTR(C_LBU, CLB_FORMAT, LOAD, RV32ZCB, UIMM) +`DEFINE_ZCB_INSTR(C_LHU, CLH_FORMAT, LOAD, RV32ZCB, UIMM) +`DEFINE_ZCB_INSTR(C_LH, CLH_FORMAT, LOAD, RV32ZCB, UIMM) +`DEFINE_ZCB_INSTR(C_SB, CSB_FORMAT, STORE, RV32ZCB, UIMM) +`DEFINE_ZCB_INSTR(C_SH, CSH_FORMAT, STORE, RV32ZCB, UIMM) +`DEFINE_ZCB_INSTR(C_ZEXT_B, CU_FORMAT, ARITHMETIC, RV32ZCB) +`DEFINE_ZCB_INSTR(C_SEXT_B, CU_FORMAT, ARITHMETIC, RV32ZCB) +`DEFINE_ZCB_INSTR(C_ZEXT_H, CU_FORMAT, ARITHMETIC, RV32ZCB) +`DEFINE_ZCB_INSTR(C_SEXT_H, CU_FORMAT, ARITHMETIC, RV32ZCB) +`DEFINE_ZCB_INSTR(C_NOT, CU_FORMAT, LOGICAL, RV32ZCB) +`DEFINE_ZCB_INSTR(C_MUL, CA_FORMAT, ARITHMETIC, RV32ZCB) + diff --git a/vendor/google_riscv-dv/src/isa/rv32zcmp_instr.sv b/vendor/google_riscv-dv/src/isa/rv32zcmp_instr.sv new file mode 100644 index 0000000000..9faf550303 --- /dev/null +++ b/vendor/google_riscv-dv/src/isa/rv32zcmp_instr.sv @@ -0,0 +1,24 @@ +/* + * Copyright 2020 Google LLC + * Copyright 2023 Frontgrade Gaisler + * + * 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. + */ + +`DEFINE_ZCMP_INSTR(CM_PUSH, CMPP_FORMAT, STORE, RV32ZCMP) +`DEFINE_ZCMP_INSTR(CM_POP, CMPP_FORMAT, LOAD, RV32ZCMP) +`DEFINE_ZCMP_INSTR(CM_POPRETZ, CMPP_FORMAT, LOAD, RV32ZCMP) +`DEFINE_ZCMP_INSTR(CM_POPRET, CMPP_FORMAT, LOAD, RV32ZCMP) +`DEFINE_ZCMP_INSTR(CM_MVA01S, CMMV_FORMAT, ARITHMETIC, RV32ZCMP) +`DEFINE_ZCMP_INSTR(CM_MVSA01, CMMV_FORMAT, ARITHMETIC, RV32ZCMP) + diff --git a/vendor/google_riscv-dv/src/isa/rv64zcb_instr.sv b/vendor/google_riscv-dv/src/isa/rv64zcb_instr.sv new file mode 100644 index 0000000000..8225b82358 --- /dev/null +++ b/vendor/google_riscv-dv/src/isa/rv64zcb_instr.sv @@ -0,0 +1,18 @@ +/* + * Copyright 2020 Google LLC + * Copyright 2023 Frontgrade Gaisler + * + * 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. + */ + +`DEFINE_ZCB_INSTR(C_ZEXT_W, CU_FORMAT, ARITHMETIC, RV64ZCB) diff --git a/vendor/google_riscv-dv/src/riscv_asm_program_gen.sv b/vendor/google_riscv-dv/src/riscv_asm_program_gen.sv index 4c969241a1..fbcc97c3be 100644 --- a/vendor/google_riscv-dv/src/riscv_asm_program_gen.sv +++ b/vendor/google_riscv-dv/src/riscv_asm_program_gen.sv @@ -459,6 +459,7 @@ class riscv_asm_program_gen extends uvm_object; RV32X, RV64X : misa[MISA_EXT_X] = 1'b1; RV32ZBA, RV32ZBB, RV32ZBC, RV32ZBS, RV64ZBA, RV64ZBB, RV64ZBC, RV64ZBS : ; // No Misa bit for Zb* extensions + RV32ZCB, RV64ZCB, RV32ZCMP, RV64ZCMP : ; // No Misa bit for Zc* extensions default : `uvm_fatal(`gfn, $sformatf("%0s is not yet supported", supported_isa[i].name())) endcase diff --git a/vendor/google_riscv-dv/src/riscv_defines.svh b/vendor/google_riscv-dv/src/riscv_defines.svh index 4d078e164b..39cfd10e1f 100644 --- a/vendor/google_riscv-dv/src/riscv_defines.svh +++ b/vendor/google_riscv-dv/src/riscv_defines.svh @@ -129,3 +129,13 @@ `define DEFINE_ZBS_INSTR(instr_n, instr_format, instr_category, instr_group, imm_tp = IMM) \ class riscv_``instr_n``_instr extends riscv_zbs_instr; \ `INSTR_BODY(instr_n, instr_format, instr_category, instr_group, imm_tp) + +//Zcb-extension instruction +`define DEFINE_ZCB_INSTR(instr_n, instr_format, instr_category, instr_group, imm_tp = IMM) \ + class riscv_``instr_n``_instr extends riscv_zcb_instr; \ + `INSTR_BODY(instr_n, instr_format, instr_category, instr_group, imm_tp) + +//Zcb-extension instruction +`define DEFINE_ZCMP_INSTR(instr_n, instr_format, instr_category, instr_group, imm_tp = IMM) \ + class riscv_``instr_n``_instr extends riscv_zcmp_instr; \ + `INSTR_BODY(instr_n, instr_format, instr_category, instr_group, imm_tp) diff --git a/vendor/google_riscv-dv/src/riscv_directed_instr_lib.sv b/vendor/google_riscv-dv/src/riscv_directed_instr_lib.sv index f3bc7080ae..7c3a54f71e 100644 --- a/vendor/google_riscv-dv/src/riscv_directed_instr_lib.sv +++ b/vendor/google_riscv-dv/src/riscv_directed_instr_lib.sv @@ -474,7 +474,8 @@ class riscv_int_numeric_corner_stream extends riscv_directed_instr_stream; for (int i = 0; i < num_of_instr; i++) begin riscv_instr instr = riscv_instr::get_rand_instr( .include_category({ARITHMETIC}), - .exclude_group({RV32C, RV64C, RV32F, RV64F, RV32D, RV64D})); + .exclude_group({RV32C, RV64C, RV32ZCB, RV64ZCB, RV32ZCMP, RV64ZCMP, + RV32F, RV64F, RV32D, RV64D})); randomize_gpr(instr); instr_list.push_back(instr); end diff --git a/vendor/google_riscv-dv/src/riscv_illegal_instr.sv b/vendor/google_riscv-dv/src/riscv_illegal_instr.sv index 60ff4b546c..d9977b81fd 100644 --- a/vendor/google_riscv-dv/src/riscv_illegal_instr.sv +++ b/vendor/google_riscv-dv/src/riscv_illegal_instr.sv @@ -213,6 +213,53 @@ class riscv_illegal_instr extends uvm_object; } } + constraint zcb_extension_c { + // zcb adds instructions to the reserved funct6 space 6'b100111 + // this constraint defines the subset of that op-code space which is still + // reserved. Only way to trigger this constraint should be with + // kReservedC0/C1 or by kIllegalCompressedOpcode. Since the reserved + // instructions at c_msb = 3'b100 && c_op = 2'b00 are not added to + // legal_c00_opcode this combination could occur. + if (RV32ZCB inside {supported_isa}) { + if (exception inside {kIllegalCompressedOpcode, kReservedCompressedInstr} + && c_msb == 3'b100) { + if (c_op == 2'b00) { + !(instr_bin[12:10] inside {3'b000, 3'b001, 3'b010, 3'b011}); //load/store + // bits 9:2 don't have any inavlid combinations, hence only bit 12:10 + // can yield an op-code which is reserved. + } else if (c_op == 2'b01 && instr_bin[12:10] == 3'b111) { + // if funct2 = 2'b10 there are no reserved op-codes + !(instr_bin[6:5] inside {2'b10}); //funct2 + if (instr_bin[6:5] == 2'b11) { + if (XLEN == 32){ + // c.zext.w not allowed in 32-bit mode + (instr_bin[4:2] inside {3'b100, 3'b110, 3'b111}); + } else { + // reserved opcodes + (instr_bin[4:2] inside {3'b110, 3'b111}); + } + } + } + } + } + } + + constraint zcmp_extension_c { + // zcmp adds instructions where funct3/c_msb = 3'b101 and c2/c_op = 2'b10 + // Those codes are legal if they match a valid Zcmp instruction + if (RV32ZCMP inside {supported_isa}) { + if (exception inside {kIllegalCompressedOpcode, kReservedCompressedInstr}) { + if (c_op == 2'b10 && c_msb == 3'b101) { + !(instr_bin[12:8] inside {5'b11000, 5'b11010, 5'b11100, 5'b11110}); // push/pop + if (instr_bin[12:10] == 3'b011) { + // double move instructions + !(instr_bin[6:5] inside {2'b01, 2'b11}); + } + } + } + } + } + constraint illegal_compressed_op_c { if (exception == kIllegalCompressedOpcode) { c_op != 2'b01; diff --git a/vendor/google_riscv-dv/src/riscv_instr_cover_group.sv b/vendor/google_riscv-dv/src/riscv_instr_cover_group.sv index badc423d65..d704456eec 100644 --- a/vendor/google_riscv-dv/src/riscv_instr_cover_group.sv +++ b/vendor/google_riscv-dv/src/riscv_instr_cover_group.sv @@ -38,6 +38,8 @@ `define SAMPLE_ZBB(cg, val) `SAMPLE_W_TYPE(cg, val, riscv_zbb_instr) `define SAMPLE_ZBC(cg, val) `SAMPLE_W_TYPE(cg, val, riscv_zbc_instr) `define SAMPLE_ZBS(cg, val) `SAMPLE_W_TYPE(cg, val, riscv_zbs_instr) +`define SAMPLE_ZCB(cg, val) `SAMPLE_W_TYPE(cg, val, riscv_zcb_instr) +`define SAMPLE_ZCMP(cg, val) `SAMPLE_W_TYPE(cg, val, riscv_zcmp_instr) `define INSTR_CG_BEGIN(INSTR_NAME, INSTR_CLASS = riscv_instr) \ covergroup ``INSTR_NAME``_cg with function sample(INSTR_CLASS instr); @@ -433,6 +435,20 @@ cp_rd : coverpoint instr.rd; \ `DV(cp_gpr_hazard : coverpoint instr.gpr_hazard;) \ +`define ZCB_I_INSTR_CG_BEGIN(INSTR_NAME) \ + `INSTR_CG_BEGIN(INSTR_NAME) \ + cp_rs1 : coverpoint instr.rs1 { \ + bins gpr[] = {S0, S1, A0, A1, A2, A3, A4, A5}; \ + } \ + `DV(cp_gpr_hazard : coverpoint instr.gpr_hazard;) + +`define ZCMP_I_INSTR_CG_BEGIN(INSTR_NAME) \ + `INSTR_CG_BEGIN(INSTR_NAME) \ + cp_rs1 : coverpoint instr.rs1 { \ + bins gpr[] = {S0, S1, A0, A1, A2, A3, A4, A5}; \ + } \ + `DV(cp_gpr_hazard : coverpoint instr.gpr_hazard;) + `define B_I_INSTR_CG_BEGIN(INSTR_NAME) \ `INSTR_CG_BEGIN(INSTR_NAME, riscv_b_instr) \ cp_rs1 : coverpoint instr.rs1; \ @@ -1646,6 +1662,64 @@ class riscv_instr_cover_group; `CA_INSTR_CG_BEGIN(c_addw) `CG_END + // RV32ZCB + `CL_INSTR_CG_BEGIN(c_lbu) + `CG_END + + `CL_INSTR_CG_BEGIN(c_lhu) + `CG_END + + `CL_INSTR_CG_BEGIN(c_lh) + `CG_END + + `CS_INSTR_CG_BEGIN(c_sb) + `CG_END + + `CS_INSTR_CG_BEGIN(c_sh) + `CG_END + + // Similar to zbb with the exception of which registers can be used + `ZCB_I_INSTR_CG_BEGIN(c_zext_b) + `CG_END + + `ZCB_I_INSTR_CG_BEGIN(c_sext_b) + `CG_END + + `ZCB_I_INSTR_CG_BEGIN(c_zext_h) + `CG_END + + `ZCB_I_INSTR_CG_BEGIN(c_sext_h) + `CG_END + + `ZCB_I_INSTR_CG_BEGIN(c_not) + `CG_END + + `CA_INSTR_CG_BEGIN(c_mul) + `CG_END + + // RV64ZCB + `ZCB_I_INSTR_CG_BEGIN(c_zext_w) + `CG_END + + // TODO This needs more thought and probably different defines per type + `ZCMP_I_INSTR_CG_BEGIN(cm_push) + `CG_END + + `ZCMP_I_INSTR_CG_BEGIN(cm_pop) + `CG_END + + `ZCMP_I_INSTR_CG_BEGIN(cm_popretz) + `CG_END + + `ZCMP_I_INSTR_CG_BEGIN(cm_popret) + `CG_END + + `ZCMP_I_INSTR_CG_BEGIN(cm_mva01s) + `CG_END + + `ZCMP_I_INSTR_CG_BEGIN(cm_mvsa01) + `CG_END + `INSTR_CG_BEGIN(hint) cp_hint : coverpoint instr.binary[15:0] { wildcard bins addi = {16'b0000_1xxx_x000_0001, @@ -2071,6 +2145,29 @@ class riscv_instr_cover_group; bseti_cg = new(); `CG_SELECTOR_END + `CG_SELECTOR_BEGIN(RV32ZCB) + c_lbu_cg = new(); + c_lhu_cg = new(); + c_lh_cg = new(); + c_sb_cg = new(); + c_sh_cg = new(); + c_zext_b_cg = new(); + c_sext_b_cg = new(); + c_zext_h_cg = new(); + c_sext_h_cg = new(); + c_not_cg = new(); + c_mul_cg = new(); + `CG_SELECTOR_END + + `CG_SELECTOR_BEGIN(RV32ZCMP) + cm_push_cg = new(); + cm_pop_cg = new(); + cm_popretz_cg = new(); + cm_popret_cg = new(); + cm_mva01s_cg = new(); + cm_mvsa01_cg = new(); + `CG_SELECTOR_END + `CG_SELECTOR_BEGIN(RV32B) pack_cg = new(); packh_cg = new(); @@ -2118,6 +2215,10 @@ class riscv_instr_cover_group; roriw_cg = new(); `CG_SELECTOR_END + `CG_SELECTOR_BEGIN(RV64ZCB) + c_zext_w_cg = new(); + `CG_SELECTOR_END + `CG_SELECTOR_BEGIN(RV64B) packw_cg = new(); packuw_cg = new(); @@ -2396,6 +2497,25 @@ class riscv_instr_cover_group; BINVI : `SAMPLE_ZBS(binvi_cg, instr) BSET : `SAMPLE_ZBS(bset_cg, instr) BSETI : `SAMPLE_ZBS(bseti_cg, instr) + // RV32ZCB + C_LBU : `SAMPLE_ZCB(c_lbu_cg, instr) + C_LHU : `SAMPLE_ZCB(c_lhu_cg, instr) + C_LH : `SAMPLE_ZCB(c_lh_cg, instr) + C_SB : `SAMPLE_ZCB(c_sb_cg, instr) + C_SH : `SAMPLE_ZCB(c_sh_cg, instr) + C_ZEXT_B : `SAMPLE_ZCB(c_zext_b_cg, instr) + C_SEXT_B : `SAMPLE_ZCB(c_sext_b_cg, instr) + C_ZEXT_H : `SAMPLE_ZCB(c_zext_h_cg, instr) + C_SEXT_H : `SAMPLE_ZCB(c_sext_h_cg, instr) + C_NOT : `SAMPLE_ZCB(c_not_cg, instr) + C_MUL : `SAMPLE_ZCB(c_mul_cg, instr) + // RV32ZCMP + CM_PUSH : `SAMPLE_ZCMP(cm_push_cg, instr) + CM_POP : `SAMPLE_ZCMP(cm_pop_cg, instr) + CM_POPRETZ : `SAMPLE_ZCMP(cm_popretz_cg, instr) + CM_POPRET : `SAMPLE_ZCMP(cm_popret_cg, instr) + CM_MVA01S : `SAMPLE_ZCMP(cm_mva01s_cg, instr) + CM_MVSA01 : `SAMPLE_ZCMP(cm_mvsa01_cg, instr) // RV32B PACK : `SAMPLE_B(pack_cg, instr) PACKH : `SAMPLE_B(packh_cg, instr) @@ -2438,6 +2558,8 @@ class riscv_instr_cover_group; ROLW : `SAMPLE_ZBB(rolw_cg, instr) RORW : `SAMPLE_ZBB(rorw_cg, instr) RORIW : `SAMPLE_ZBB(roriw_cg, instr) + // RV64ZCB + C_ZEXT_W : `SAMPLE_ZCB(c_zext_w_cg, instr) // RV64B PACKW : `SAMPLE_B(packw_cg, instr) PACKUW : `SAMPLE_B(packuw_cg, instr) @@ -2544,8 +2666,8 @@ class riscv_instr_cover_group; if ((instr.group inside {supported_isa}) && (instr.group inside {RV32I, RV32M, RV64M, RV64I, RV32C, RV64C, RVV, RV64B, RV32B, - RV32ZBA, RV32ZBB, RV32ZBC, RV32ZBS, - RV64ZBA, RV64ZBB, RV64ZBC, RV64ZBS})) begin + RV32ZBA, RV32ZBB, RV32ZBC, RV32ZBS, RV32ZCB, RV32ZCMP, + RV64ZBA, RV64ZBB, RV64ZBC, RV64ZBS, RV64ZCB, RV64ZCMP})) begin if (((instr_name inside {URET}) && !support_umode_trap) || ((instr_name inside {SRET, SFENCE_VMA}) && !(SUPERVISOR_MODE inside {supported_privileged_mode})) || diff --git a/vendor/google_riscv-dv/src/riscv_instr_gen_config.sv b/vendor/google_riscv-dv/src/riscv_instr_gen_config.sv index 0712821e58..4b7cab531c 100644 --- a/vendor/google_riscv-dv/src/riscv_instr_gen_config.sv +++ b/vendor/google_riscv-dv/src/riscv_instr_gen_config.sv @@ -264,6 +264,8 @@ class riscv_instr_gen_config extends uvm_object; bit enable_zbb_extension; bit enable_zbc_extension; bit enable_zbs_extension; + bit enable_zcb_extension; + bit enable_zcmp_extension; b_ext_group_t enable_bitmanip_groups[] = {ZBB, ZBS, ZBP, ZBE, ZBF, ZBC, ZBR, ZBM, ZBT, ZB_TMP}; @@ -541,6 +543,8 @@ class riscv_instr_gen_config extends uvm_object; `uvm_field_int(enable_zbb_extension, UVM_DEFAULT) `uvm_field_int(enable_zbc_extension, UVM_DEFAULT) `uvm_field_int(enable_zbs_extension, UVM_DEFAULT) + `uvm_field_int(enable_zcb_extension, UVM_DEFAULT) + `uvm_field_int(enable_zcmp_extension, UVM_DEFAULT) `uvm_field_int(use_push_data_section, UVM_DEFAULT) `uvm_object_utils_end @@ -611,6 +615,8 @@ class riscv_instr_gen_config extends uvm_object; get_bool_arg_value("+enable_zbb_extension=", enable_zbb_extension); get_bool_arg_value("+enable_zbc_extension=", enable_zbc_extension); get_bool_arg_value("+enable_zbs_extension=", enable_zbs_extension); + get_bool_arg_value("+enable_zcb_extension=", enable_zcb_extension); + get_bool_arg_value("+enable_zcmp_extension=", enable_zcmp_extension); cmdline_enum_processor #(b_ext_group_t)::get_array_values("+enable_bitmanip_groups=", 1'b0, enable_bitmanip_groups); if(inst.get_arg_value("+boot_mode=", boot_mode_opts)) begin @@ -657,6 +663,16 @@ class riscv_instr_gen_config extends uvm_object; enable_zbs_extension = 0; end + + if (!((RV32ZCB inside {supported_isa}) || + (RV64ZCB inside {supported_isa}))) begin + enable_zcb_extension = 0; + end + + if (!((RV32ZCMP inside {supported_isa}) || + (RV64ZCMP inside {supported_isa}))) begin + enable_zcmp_extension = 0; + end vector_cfg = riscv_vector_cfg::type_id::create("vector_cfg"); pmp_cfg = riscv_pmp_cfg::type_id::create("pmp_cfg"); pmp_cfg.rand_mode(pmp_cfg.pmp_randomize); diff --git a/vendor/google_riscv-dv/src/riscv_instr_pkg.sv b/vendor/google_riscv-dv/src/riscv_instr_pkg.sv index 12f248b74e..fd36ea63ce 100644 --- a/vendor/google_riscv-dv/src/riscv_instr_pkg.sv +++ b/vendor/google_riscv-dv/src/riscv_instr_pkg.sv @@ -80,7 +80,7 @@ package riscv_instr_pkg; MACHINE_MODE = 2'b11 } privileged_mode_t; - typedef enum bit [4:0] { + typedef enum bit [5:0] { RV32I, RV64I, RV32M, @@ -108,6 +108,12 @@ package riscv_instr_pkg; RV64ZBB, RV64ZBC, RV64ZBS, + RV32ZCB, + RV64ZCB, + RV32ZCMP, + RV64ZCMP, + RV32ZMMUL, + RV64ZMMUL, RV32X, RV64X } riscv_instr_group_t; @@ -271,6 +277,27 @@ package riscv_instr_pkg; PACKW, PACKUW, XPERM_W, + // RV32ZCB + C_LBU, + C_LHU, + C_LH, + C_SB, + C_SH, + C_ZEXT_B, + C_SEXT_B, + C_ZEXT_H, + C_SEXT_H, + C_NOT, + C_MUL, + // RV64ZCB instructions + C_ZEXT_W, + // RV32ZCMP + CM_PUSH, + CM_POP, + CM_POPRETZ, + CM_POPRET, + CM_MVA01S, + CM_MVSA01, // RV32M instructions MUL, MULH, @@ -665,6 +692,8 @@ package riscv_instr_pkg; S2, S3, S4, S5, S6, S7, S8, S9, S10, S11, T3, T4, T5, T6 } riscv_reg_t; + typedef riscv_reg_t riscv_reglist_t[$]; + typedef enum bit [4:0] { FT0, FT1, FT2, FT3, FT4, FT5, FT6, FT7, FS0, FS1, FA0, FA1, FA2, FA3, FA4, FA5, FA6, FA7, FS2, FS3, FS4, FS5, FS6, FS7, FS8, FS9, FS10, FS11, FT8, FT9, FT10, FT11 @@ -693,6 +722,14 @@ package riscv_instr_pkg; CS_FORMAT, CSS_FORMAT, CIW_FORMAT, + // Zc compressed instruction format + CLB_FORMAT, + CSB_FORMAT, + CLH_FORMAT, + CSH_FORMAT, + CU_FORMAT, + CMMV_FORMAT, + CMPP_FORMAT, // Vector instruction format VSET_FORMAT, VA_FORMAT, @@ -1535,6 +1572,8 @@ package riscv_instr_pkg; typedef class riscv_zbb_instr; typedef class riscv_zbc_instr; typedef class riscv_zbs_instr; + typedef class riscv_zcb_instr; + typedef class riscv_zcmp_instr; typedef class riscv_b_instr; `include "riscv_instr_gen_config.sv" `include "isa/riscv_instr.sv" @@ -1560,6 +1599,11 @@ package riscv_instr_pkg; `include "isa/rv32zbb_instr.sv" `include "isa/rv32zbc_instr.sv" `include "isa/rv32zbs_instr.sv" + `include "isa/riscv_zcb_instr.sv" + `include "isa/rv32zcb_instr.sv" + `include "isa/rv64zcb_instr.sv" + `include "isa/riscv_zcmp_instr.sv" + `include "isa/rv32zcmp_instr.sv" `include "isa/rv32m_instr.sv" `include "isa/rv64a_instr.sv" `include "isa/rv64b_instr.sv" diff --git a/vendor/google_riscv-dv/src/riscv_instr_stream.sv b/vendor/google_riscv-dv/src/riscv_instr_stream.sv index 9e980da87d..0a7f15fc70 100644 --- a/vendor/google_riscv-dv/src/riscv_instr_stream.sv +++ b/vendor/google_riscv-dv/src/riscv_instr_stream.sv @@ -228,24 +228,36 @@ class riscv_rand_instr_stream extends riscv_instr_stream; end endfunction - function void randomize_instr(output riscv_instr instr, - input bit is_in_debug = 1'b0, - input bit disable_dist = 1'b0, - input riscv_instr_group_t include_group[$] = {}); - riscv_instr_name_t exclude_instr[]; + function void update_excluded_instr(ref riscv_instr_name_t exclude_instr[], + input bit is_in_debug = 1'b0); if ((SP inside {reserved_rd, cfg.reserved_regs}) || ((avail_regs.size() > 0) && !(SP inside {avail_regs}))) begin - exclude_instr = {C_ADDI4SPN, C_ADDI16SP, C_LWSP, C_LDSP}; + exclude_instr = {exclude_instr, C_ADDI4SPN, C_ADDI16SP, C_LWSP, C_LDSP}; end - // Post-process the allowed_instr and exclude_instr lists to handle - // adding ebreak instructions to the debug rom. + if ((A0 inside {reserved_rd, cfg.reserved_regs}) || + (A1 inside {reserved_rd, cfg.reserved_regs}) || + ((avail_regs.size() > 0) && (!(A0 inside {avail_regs}) || !(A1 inside {avail_regs})))) begin + // MVA01S instruction needs both A0 and A1 to be writable + exclude_instr = {exclude_instr, CM_MVA01S}; + end + // Post-process the exclude_instr lists to handle adding ebreak instructions to the debug rom. if (is_in_debug) begin - if (cfg.no_ebreak && cfg.enable_ebreak_in_debug_rom) begin - allowed_instr = {allowed_instr, EBREAK, C_EBREAK}; - end else if (!cfg.no_ebreak && !cfg.enable_ebreak_in_debug_rom) begin + if (!cfg.no_ebreak && !cfg.enable_ebreak_in_debug_rom) begin exclude_instr = {exclude_instr, EBREAK, C_EBREAK}; end end + endfunction + + function void randomize_instr(output riscv_instr instr, + input bit is_in_debug = 1'b0, + input bit disable_dist = 1'b0, + input riscv_instr_group_t include_group[$] = {}); + riscv_instr_name_t exclude_instr[]; + update_excluded_instr(exclude_instr, is_in_debug); + // Post-process the allowed_instr lists to handle adding ebreak instructions to the debug rom. + if (is_in_debug && cfg.no_ebreak && cfg.enable_ebreak_in_debug_rom) begin + allowed_instr = {allowed_instr, EBREAK, C_EBREAK}; + end instr = riscv_instr::get_rand_instr(.include_instr(allowed_instr), .exclude_instr(exclude_instr), .include_group(include_group)); @@ -256,30 +268,48 @@ class riscv_rand_instr_stream extends riscv_instr_stream; function void randomize_gpr(riscv_instr instr); `DV_CHECK_RANDOMIZE_WITH_FATAL(instr, if (avail_regs.size() > 0) { - if (has_rs1) { - rs1 inside {avail_regs}; - } - if (has_rs2) { - rs2 inside {avail_regs}; - } - if (has_rd) { - rd inside {avail_regs}; + if (format == CMMV_FORMAT) { + if (has_rs1 && has_rs2) { + rs2 != rs1; + } + rs1 inside {S0, S1, [S2:S7]}; + rs2 inside {S0, S1, [S2:S7]}; + } else { + if (has_rs1) { + rs1 inside {avail_regs}; + } + if (has_rs2) { + rs2 inside {avail_regs}; + } + if (has_rd) { + rd inside {avail_regs}; + } } } foreach (reserved_rd[i]) { if (has_rd) { rd != reserved_rd[i]; } - if (format == CB_FORMAT) { + if (instr_name == CM_MVSA01) { rs1 != reserved_rd[i]; + rs2 != reserved_rd[i]; + } + if (instr_name == CM_MVA01S) { + A0 != reserved_rd[i]; + A1 != reserved_rd[i]; } } foreach (cfg.reserved_regs[i]) { if (has_rd) { rd != cfg.reserved_regs[i]; } - if (format == CB_FORMAT) { + if (instr_name == CM_MVSA01) { rs1 != cfg.reserved_regs[i]; + rs2 != cfg.reserved_regs[i]; + } + if (instr_name == CM_MVA01S) { + A0 != cfg.reserved_regs[i]; + A1 != cfg.reserved_regs[i]; } } // TODO: Add constraint for CSR, floating point register diff --git a/vendor/google_riscv-dv/src/riscv_load_store_instr_lib.sv b/vendor/google_riscv-dv/src/riscv_load_store_instr_lib.sv index cae0873137..6dcbbfdd3d 100644 --- a/vendor/google_riscv-dv/src/riscv_load_store_instr_lib.sv +++ b/vendor/google_riscv-dv/src/riscv_load_store_instr_lib.sv @@ -119,19 +119,33 @@ class riscv_load_store_base_instr_stream extends riscv_mem_access_stream; // Generate each load/store instruction virtual function void gen_load_store_instr(); - bit enable_compressed_load_store; + bit enable_compressed_load_store, enable_zcb; riscv_instr instr; randomize_avail_regs(); if ((rs1_reg inside {[S0 : A5], SP}) && !cfg.disable_compressed_instr) begin enable_compressed_load_store = 1; end + if ((RV32C inside {riscv_instr_pkg::supported_isa}) && + (RV32ZCB inside {riscv_instr_pkg::supported_isa} && cfg.enable_zcb_extension)) begin + enable_zcb = 1; + end foreach (addr[i]) begin // Assign the allowed load/store instructions based on address alignment // This is done separately rather than a constraint to improve the randomization performance allowed_instr = {LB, LBU, SB}; + if((offset[i] inside {[0:2]}) && enable_compressed_load_store && + enable_zcb && rs1_reg != SP) begin + `uvm_info(`gfn, "Add ZCB byte load/store to allowed instr", UVM_LOW) + allowed_instr = {C_LBU, C_SB}; + end if (!cfg.enable_unaligned_load_store) begin if (addr[i][0] == 1'b0) begin allowed_instr = {LH, LHU, SH, allowed_instr}; + if(((offset[i] == 0) || (offset[i] == 2)) && enable_compressed_load_store && + enable_zcb && rs1_reg != SP) begin + `uvm_info(`gfn, "Add ZCB half-word load/store to allowed instr", UVM_LOW) + allowed_instr = {C_LHU, C_LH, C_SH}; + end end if (addr[i] % 4 == 0) begin allowed_instr = {LW, SW, allowed_instr}; @@ -682,3 +696,269 @@ class riscv_vector_load_store_instr_stream extends riscv_mem_access_stream; endfunction endclass + +// Class to test Zcmp Push/Popret control flow chains +class riscv_zcmp_chain_instr_stream extends riscv_mem_access_stream; + // Number of push/pop blocks in the chain + rand int unsigned num_blocks; + // Execution order of these blocks + int unsigned block_order[]; + + // Track number of chains + static int unsigned chain_cnt = 0; + int unsigned chain_id; + + // Data page to use for stack operations + rand int base; + rand int unsigned data_page_id; + rand int unsigned max_load_store_offset; + + constraint addr_c { + solve data_page_id before max_load_store_offset; + solve max_load_store_offset before base; + data_page_id < max_data_page_id; + foreach (data_page[i]) { + if (i == data_page_id) {max_load_store_offset == data_page[i].size_in_bytes;} + } + base inside {[0 : max_load_store_offset - 1]}; + } + + constraint block_c {num_blocks inside {[1 : 50]};} + + function new(string name = ""); + super.new(name); + // Assign a unique ID to this specific stream instance + chain_id = chain_cnt++; + endfunction + + `uvm_object_utils(riscv_zcmp_chain_instr_stream) + + function void post_randomize(); + // SP cannot be modified by other instructions + if (!(SP inside {reserved_rd})) begin + reserved_rd = {reserved_rd, SP}; + end + setup_chain_order(); + + // Generate the chain of blocks + gen_zcmp_chain(); + // Ensure labels are preserved + foreach (instr_list[i]) begin + if (instr_list[i].label != "") instr_list[i].has_label = 1'b1; + else instr_list[i].has_label = 1'b0; + instr_list[i].atomic = 1'b1; + end + // Initizialize SP to point to a data page + add_rs1_init_la_instr(SP, data_page_id, 0); + // Don't call super here, because it will delete all labels + endfunction + + // Randomize the order in which blocks are executed + virtual function void setup_chain_order(); + block_order = new[num_blocks]; + foreach (block_order[i]) begin + block_order[i] = i; + end + block_order.shuffle(); + endfunction + + // Main generator function + virtual function void gen_zcmp_chain(); + riscv_instr instr; + riscv_instr popret_instr; + string prefix = $sformatf("zcmp_%0d", chain_id); + riscv_instr_name_t exclude_instr[]; + update_excluded_instr(exclude_instr); + + // Save all registers that will be overwritten by our upcoming push/pop chain + // A0 is overwritten by CM.POPRETZ, but cannot be saved by CM.PUSH/POP. + // Save A0 outside the main stack frame (e.g., at SP-120) + instr = riscv_instr::get_instr(ADDI); + instr.rd = SP; + instr.rs1 = SP; + instr.imm_str = "-16"; + instr_list.push_back(instr); + instr = riscv_instr::get_instr(XLEN == 32 ? SW : SD); + instr.rs1 = SP; + instr.rs2 = A0; + instr.imm_str = "0"; + instr_list.push_back(instr); + // Save all the remaining registers overwritten by pop + instr = riscv_instr::get_instr(CM_PUSH); + instr.stack_adj = -112; // Max stack frame + instr.rlist = 15; // All registers + instr_list.push_back(instr); + // Jump to the first block + instr = riscv_instr::get_instr(JAL); + instr.rd = ZERO; // Don't link, just jump + instr.imm_str = $sformatf("%s_%0d", prefix, block_order[0]); + instr.comment = "Bootstrap: Jump to first random block"; + instr_list.push_back(instr); + + // Generate the push/pop blocks + for (int i = 0; i < num_blocks; i++) begin + // Number of random instructions to insert between push/pop + int random_instructions; + // Labels to link blocks + string current_label = $sformatf("%s_%0d", prefix, i); + string next_label; + int next_block_idx = -1; + // Control the stack growth/shrinkage + // Total number of instructions (Push + Pop) + int num_steps; + // Stack size at each step + int stack_size[]; + int max_stack = max_load_store_offset; + int delta; + riscv_instr_name_t final_opcode; + + // Find which block comes after current block 'i' in the execution order + foreach (block_order[k]) begin + if (block_order[k] == i) begin + if (k < num_blocks - 1) begin + next_block_idx = block_order[k+1]; + next_label = $sformatf("%s_%0d", prefix, next_block_idx); + end else begin + // End of chain + next_label = $sformatf("%s_end", prefix); + end + end + end + + // Randomize the stack pointer's path and the number of steps + std::randomize(stack_size, num_steps) with { + // Number of push/pop steps per block + num_steps inside {[2:12]}; + // Include the initial zero depth + stack_size.size() == num_steps + 1; + // Start and stop at zero depth + stack_size[0] == 0; + stack_size[num_steps] == 0; + foreach (stack_size[i]) { + // Stay within stack region + stack_size[i] inside {[-max_stack:0]}; + // Transition Constraints + if (i > 0) { + // Must be 16-byte aligned steps + (stack_size[i] - stack_size[i-1]) % 16 == 0; + // Restrict adjustment size of single push/pop and avoid no-ops (diff==0) + (stack_size[i] - stack_size[i-1]) inside {[-112:-16], [16:112]}; + } + } + }; + + // Loop through all the steps and insert a push or pop instruction. Also sprinkle in some + // random instructions inbetween. Skip the very last pop, since that will be a popret we + // handle specially. + for (int i = 0; i < num_steps - 1; i++) begin + delta = stack_size[i+1] - stack_size[i]; + if (delta < 0) begin + // CM.PUSH (Stack Grows) + instr = riscv_instr::get_instr(CM_PUSH); + `DV_CHECK_RANDOMIZE_WITH_FATAL(instr, stack_adj == delta;) + end else begin + // CM.POP (Stack Shrinks) + instr = riscv_instr::get_instr(CM_POP); + `DV_CHECK_RANDOMIZE_WITH_FATAL(instr, stack_adj == delta;) + end + + // First instruction gets the label to jump to + if (i == 0) begin + instr.label = current_label; + end + + instr_list.push_back(instr); + + // Insert random instructions in between push and pops + random_instructions = $urandom_range(0, 5); + repeat (random_instructions) begin + instr = riscv_instr::get_rand_instr(.include_category({ARITHMETIC, LOGICAL, SHIFT, COMPARE + }), .exclude_instr(exclude_instr)); + `DV_CHECK_RANDOMIZE_WITH_FATAL(instr, + // CRITICAL: Do not touch SP! + if (cfg.reserved_regs.size() > 0 || reserved_rd.size() > 0) { + !(rd inside {cfg.reserved_regs, reserved_rd}); + }, + $sformatf("Cannot randomize instruction %s with constrained registers\n", + instr.convert2asm())) + instr_list.push_back(instr); + end + end + + // Last step is a POPRET/POPRETZ. Randomly choose which: + randcase + 1: final_opcode = CM_POPRET; + 1: final_opcode = CM_POPRETZ; + endcase + delta = stack_size[num_steps] - stack_size[num_steps-1]; + // Do not insert this popret into the instruction stream yet. First, we have to create a + // proper RA address to jump to. However, since we need to know where the POPRET will load the + // RA from (i.e., which rlist and stack_adj it uses), we have to randomize it first. + popret_instr = riscv_instr::get_instr(final_opcode); + `DV_CHECK_RANDOMIZE_WITH_FATAL(popret_instr, stack_adj == delta;) + + // Push the RA with the address of 'next_label' onto the stack at the correct location. The + // following instructiosn are "atomic" because we don't want other instructions to interleave + // between them. + if (next_label != "") begin + riscv_pseudo_instr la_instr_pseudo; + riscv_reg_t temp_reg; + int ra_offset_in_stack; + + // Calculate where RA will be read from by the POPRET instruction. + // For cm.pop {ra, s0-sN}, stack_adj: + // RA is usually at `[SP + stack_adj - XLEN/8*(rlist - 3)]` because we pop rlist-3 registers + // There is one exception, if rlist == 15 (all registers), we pop rlist-2 registers because + // it includes s10 and s11. So adjust accordingly. + ra_offset_in_stack = delta - XLEN / 8 * (popret_instr.rlist - 3); + if (popret_instr.rlist == 15) begin + ra_offset_in_stack = ra_offset_in_stack - XLEN / 8; + end + + // Get a temp reg that isn't reserved and not X0 + `DV_CHECK_STD_RANDOMIZE_WITH_FATAL( + temp_reg, !(temp_reg inside {cfg.reserved_regs, reserved_rd, ZERO});) + + // Load Address of the NEXT block + la_instr_pseudo = riscv_pseudo_instr::type_id::create("la_instr_pseudo"); + la_instr_pseudo.pseudo_instr_name = LA; + la_instr_pseudo.rd = temp_reg; + la_instr_pseudo.imm_str = next_label; + la_instr_pseudo.atomic = 1'b1; + instr_list.push_back(la_instr_pseudo); + + // Overwrite the RA slot on the stack + instr = riscv_instr::get_instr(XLEN == 32 ? SW : SD); + instr.rs1 = SP; + instr.rs2 = temp_reg; + instr.imm_str = $sformatf("%0d", ra_offset_in_stack); + instr.atomic = 1'b1; + instr.comment = "Overwrite saved RA with next block address"; + instr_list.push_back(instr); + end + + // Insert popret at the very end. We already randomized it earlier to place the RA at the correct address. + instr_list.push_back(popret_instr); + end + + // Endpoint of chain: Restore all registers overwritten by push/pop + instr = riscv_instr::get_instr(CM_POP); + instr.stack_adj = 112; // Max stack frame + instr.rlist = 15; // All registers + instr.label = $sformatf("%s_end", prefix); + instr.comment = "End of Zcmp Chain"; + instr_list.push_back(instr); + // Restore A0 from the stack + instr = riscv_instr::get_instr(XLEN == 32 ? LW : LD); + instr.rd = A0; + instr.rs1 = SP; + instr.imm_str = "0"; + instr_list.push_back(instr); + instr = riscv_instr::get_instr(ADDI); + instr.rd = SP; + instr.rs1 = SP; + instr.imm_str = "16"; + instr_list.push_back(instr); + endfunction + +endclass diff --git a/vendor/google_riscv-dv/src/riscv_loop_instr.sv b/vendor/google_riscv-dv/src/riscv_loop_instr.sv index c2fcecc63f..507a5e7503 100644 --- a/vendor/google_riscv-dv/src/riscv_loop_instr.sv +++ b/vendor/google_riscv-dv/src/riscv_loop_instr.sv @@ -113,7 +113,10 @@ class riscv_loop_instr extends riscv_rand_instr_stream; `uvm_object_new function void post_randomize(); + riscv_instr_name_t exclude_instr[]; reserved_rd = {loop_cnt_reg, loop_limit_reg}; + // Figure out which instructions we can no longer use after the loop regs are decided + update_excluded_instr(exclude_instr); // Generate instructions that mixed with the loop instructions initialize_instr_list(num_of_instr_in_loop); gen_instr(1'b1); @@ -144,10 +147,11 @@ class riscv_loop_instr extends riscv_rand_instr_stream; // Branch target instruction, can be anything loop_branch_target_instr[i] = riscv_instr::get_rand_instr( .include_category({ARITHMETIC, LOGICAL, COMPARE}), - .exclude_instr({C_ADDI16SP})); + .exclude_instr(exclude_instr)); `DV_CHECK_RANDOMIZE_WITH_FATAL(loop_branch_target_instr[i], - if (format == CB_FORMAT) { + if (instr_name == CM_MVSA01) { !(rs1 inside {reserved_rd, cfg.reserved_regs}); + !(rs2 inside {reserved_rd, cfg.reserved_regs}); } if (has_rd) { !(rd inside {reserved_rd, cfg.reserved_regs}); diff --git a/vendor/google_riscv-dv/test/riscv_instr_cov_test.sv b/vendor/google_riscv-dv/test/riscv_instr_cov_test.sv index 7b7dc39b05..029891d287 100644 --- a/vendor/google_riscv-dv/test/riscv_instr_cov_test.sv +++ b/vendor/google_riscv-dv/test/riscv_instr_cov_test.sv @@ -138,8 +138,8 @@ class riscv_instr_cov_test extends uvm_test; instr = riscv_instr::get_instr(instr_name); if ((instr.group inside {RV32I, RV32M, RV32C, RV64I, RV64M, RV64C, RV32F, RV64F, RV32D, RV64D, RV32B, RV64B, - RV32ZBA, RV32ZBB, RV32ZBC, RV32ZBS, - RV64ZBA, RV64ZBB, RV64ZBC, RV64ZBS}) && + RV32ZBA, RV32ZBB, RV32ZBC, RV32ZBS, RV32ZCB, RV32ZCMP, + RV64ZBA, RV64ZBB, RV64ZBC, RV64ZBS, RV64ZCB, RV64ZCMP}) && (instr.group inside {supported_isa})) begin assign_trace_info_to_instr(instr); instr.pre_sample(); diff --git a/vendor/google_riscv-dv/yaml/iss.yaml b/vendor/google_riscv-dv/yaml/iss.yaml index 8a8b662307..1167d7fd92 100644 --- a/vendor/google_riscv-dv/yaml/iss.yaml +++ b/vendor/google_riscv-dv/yaml/iss.yaml @@ -15,7 +15,7 @@ - iss: spike path_var: SPIKE_PATH cmd: > - /spike --log-commits --isa= -l + /spike --log-commits --isa= --priv= --misaligned -l - iss: ovpsim path_var: OVPSIM_PATH @@ -35,9 +35,9 @@ - iss: whisper path_var: WHISPER_ISS cmd: > - --log --xlen --isa + --log --xlen --isa --configfile /whisper.json --iccmrw - iss: renode path_var: RENODE_PATH cmd: > - python3 /renode_wrapper.py --renode "" --elf --isa + python3 /renode_wrapper.py --renode "" --elf --isa --priv= --mem-size 0x80000000 diff --git a/vendor/google_riscv-dv/yaml/whisper.json b/vendor/google_riscv-dv/yaml/whisper.json new file mode 100644 index 0000000000..62551c7c67 --- /dev/null +++ b/vendor/google_riscv-dv/yaml/whisper.json @@ -0,0 +1,7 @@ +{ + "iccm": { + "region": "0", + "size": "0x80000000", + "offset": "0x80000000" + } +} From 5f3e8a6564254705d5f75fd1697251061bf4510b Mon Sep 17 00:00:00 2001 From: Samuel Riedel Date: Mon, 15 Dec 2025 16:01:08 +0000 Subject: [PATCH 10/10] [dv,vendor] Disable Zc* tests and revert compiler update related flags Temporarily disable the Zcb/Zcmp tests while we wait to upgrade the compiler. This ensures the CI can run regressions with the current compiler to verify that we do not introduce breaking changes. --- .../riscv_dv_extension/testlist.yaml | 33 ----------- dv/uvm/core_ibex/scripts/ibex_cmd.py | 3 +- dv/uvm/core_ibex/tests/core_ibex_base_test.sv | 4 -- .../google_riscv-dv/0004-compiler-flags.patch | 55 +++++++++++++++++++ 4 files changed, 56 insertions(+), 39 deletions(-) create mode 100644 vendor/patches/google_riscv-dv/0004-compiler-flags.patch diff --git a/dv/uvm/core_ibex/riscv_dv_extension/testlist.yaml b/dv/uvm/core_ibex/riscv_dv_extension/testlist.yaml index 5541640f6b..8c6516650f 100644 --- a/dv/uvm/core_ibex/riscv_dv_extension/testlist.yaml +++ b/dv/uvm/core_ibex/riscv_dv_extension/testlist.yaml @@ -1157,36 +1157,3 @@ rtl_test: core_ibex_base_test rtl_params: RV32B: ["ibex_pkg::RV32BFull", "ibex_pkg::RV32BOTEarlGrey", "ibex_pkg::RV32BBalanced"] - -- test: riscv_zcb_balanced_test - desc: > - Random instruction test with zcb instructions in balanced configuration - iterations: 10 - gen_test: riscv_rand_instr_test - gen_opts: > - +enable_zcb_extension=1 - rtl_test: core_ibex_base_test - -- test: riscv_zcmp_balanced_test - desc: > - Random instruction test with zcmp instructions in balanced configuration - iterations: 10 - gen_test: riscv_rand_instr_test - gen_opts: > - +enable_zcb_extension=1 - +enable_zcmp_extension=1 - rtl_test: core_ibex_base_test - -- test: riscv_zcmp_directed_test - desc: > - Random instruction test with zcmp instructions in balanced configuration - iterations: 10 - gen_test: riscv_instr_base_test - gen_opts: > - +enable_zcb_extension=1 - +enable_zcmp_extension=1 - +directed_instr_0=riscv_zcmp_chain_instr_stream,1 - +instr_cnt=100 - +num_of_sub_program=0 - +no_branch_jump=1 - rtl_test: core_ibex_base_test diff --git a/dv/uvm/core_ibex/scripts/ibex_cmd.py b/dv/uvm/core_ibex/scripts/ibex_cmd.py index 21c84b7b4d..0d8c5346e2 100644 --- a/dv/uvm/core_ibex/scripts/ibex_cmd.py +++ b/dv/uvm/core_ibex/scripts/ibex_cmd.py @@ -114,9 +114,8 @@ def get_isas_for_config(cfg: Config) -> Tuple[str, str]: has_bitmanip = cfg.rv32b != 'ibex_pkg::RV32BNone' toolchain_isa = base_isa + ('b' if has_bitmanip else '') - toolchain_isa = toolchain_isa + ('_zicsr_zifencei_zcb_zcmp') - return (toolchain_isa, '_'.join([base_isa] + ['Zicsr','Zifencei','Zcb','Zcmp'] + bitmanip_isa)) + return (toolchain_isa, '_'.join([base_isa] + bitmanip_isa)) _TestEntry = Dict[str, object] diff --git a/dv/uvm/core_ibex/tests/core_ibex_base_test.sv b/dv/uvm/core_ibex/tests/core_ibex_base_test.sv index 668843b0e6..47847ad671 100644 --- a/dv/uvm/core_ibex/tests/core_ibex_base_test.sv +++ b/dv/uvm/core_ibex/tests/core_ibex_base_test.sv @@ -70,10 +70,6 @@ class core_ibex_base_test extends uvm_test; isa = {"rv32", RV32E ? "e" : "i"}; if (RV32M != RV32MNone) isa = {isa, "m"}; isa = {isa, "c"}; - isa = {isa, "_Zicsr"}; - isa = {isa, "_Zifencei"}; - isa = {isa, "_Zcb"}; - isa = {isa, "_Zcmp"}; case (RV32B) RV32BNone: ; diff --git a/vendor/patches/google_riscv-dv/0004-compiler-flags.patch b/vendor/patches/google_riscv-dv/0004-compiler-flags.patch new file mode 100644 index 0000000000..86816df5d5 --- /dev/null +++ b/vendor/patches/google_riscv-dv/0004-compiler-flags.patch @@ -0,0 +1,55 @@ +--- a/run.py ++++ b/run.py +@@ -951,40 +951,40 @@ def load_config(args, cwd): + args.core_setting_dir = cwd + "/target/" + args.target + if args.target == "rv32imc": + args.mabi = "ilp32" +- args.isa = "rv32imc_zicsr_zifencei" ++ args.isa = "rv32imc" + elif args.target == "rv32imafdc": + args.mabi = "ilp32" +- args.isa = "rv32imafdc_zicsr_zifencei" ++ args.isa = "rv32imafdc" + elif args.target == "rv32imc_sv32": + args.mabi = "ilp32" +- args.isa = "rv32imc_zicsr_zifencei" ++ args.isa = "rv32imc" + elif args.target == "multi_harts": + args.mabi = "ilp32" +- args.isa = "rv32gc_zicsr_zifencei" ++ args.isa = "rv32gc" + elif args.target == "rv32imcb": + args.mabi = "ilp32" +- args.isa = "rv32imcb_zicsr_zifencei" ++ args.isa = "rv32imcb" + elif args.target == "rv32i": + args.mabi = "ilp32" +- args.isa = "rv32i_zicsr_zifencei" ++ args.isa = "rv32i" + elif args.target == "rv64imc": + args.mabi = "lp64" +- args.isa = "rv64imc_zicsr_zifencei" ++ args.isa = "rv64imc" + elif args.target == "rv64imcb": + args.mabi = "lp64" +- args.isa = "rv64imcb_zicsr_zifencei" ++ args.isa = "rv64imcb" + elif args.target == "rv64gc": + args.mabi = "lp64" +- args.isa = "rv64gc_zicsr_zifencei" ++ args.isa = "rv64gc" + elif args.target == "rv64gcv": + args.mabi = "lp64" +- args.isa = "rv64gcv_zicsr_zifencei" ++ args.isa = "rv64gcv" + elif args.target == "ml": + args.mabi = "lp64" +- args.isa = "rv64imc_zicsr_zifencei" ++ args.isa = "rv64imc" + elif args.target == "rv64imafdc": + args.mabi = "lp64" +- args.isa = "rv64imafdc_zicsr_zifencei" ++ args.isa = "rv64imafdc" + else: + sys.exit("Unsupported pre-defined target: {}".format(args.target)) + else: