diff --git a/dv/uvm/core_ibex/common/irq_agent/irq_if.sv b/dv/uvm/core_ibex/common/irq_agent/irq_if.sv index c57e615c..92de1301 100644 --- a/dv/uvm/core_ibex/common/irq_agent/irq_if.sv +++ b/dv/uvm/core_ibex/common/irq_agent/irq_if.sv @@ -11,6 +11,7 @@ interface irq_if(input clk); logic irq_nm; // non-maskeable interrupt clocking driver_cb @(posedge clk); + default output negedge; input reset; output irq_software; output irq_timer; @@ -32,4 +33,8 @@ interface irq_if(input clk); repeat (num) @(posedge clk); endtask + task automatic wait_neg_clks(input int num); + repeat (num) @(negedge clk); + endtask + endinterface diff --git a/dv/uvm/core_ibex/common/irq_agent/irq_master_driver.sv b/dv/uvm/core_ibex/common/irq_agent/irq_master_driver.sv index d695d95f..fca2cadf 100644 --- a/dv/uvm/core_ibex/common/irq_agent/irq_master_driver.sv +++ b/dv/uvm/core_ibex/common/irq_agent/irq_master_driver.sv @@ -52,7 +52,7 @@ class irq_master_driver extends uvm_driver #(irq_seq_item); drive_seq_item(rsp); seq_item_port.item_done(rsp); end else begin - vif.wait_clks(1); + vif.wait_neg_clks(1); end end endtask : get_and_drive 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 new file mode 100644 index 00000000..2ad60ef3 --- /dev/null +++ b/dv/uvm/core_ibex/env/core_ibex_instr_monitor_if.sv @@ -0,0 +1,18 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +// Interface to probe the instruction moving through the pipeline +// +// TODO: does not support dummy instruction insertion right now, +// might have to revisit and update. +interface core_ibex_instr_monitor_if #(parameter DATA_WIDTH = 32); + + // ID stage + logic valid_id; + logic err_id; + logic is_compressed_id; + logic [15:0] instr_compressed_id; + logic [DATA_WIDTH-1:0] instr_id; + +endinterface diff --git a/dv/uvm/core_ibex/ibex_dv.f b/dv/uvm/core_ibex/ibex_dv.f index 5096ec7e..a7ed7862 100644 --- a/dv/uvm/core_ibex/ibex_dv.f +++ b/dv/uvm/core_ibex/ibex_dv.f @@ -68,6 +68,7 @@ ${PRJ_DIR}/ibex/dv/uvm/core_ibex/common/ibex_mem_intf_agent/ibex_mem_intf.sv ${PRJ_DIR}/ibex/dv/uvm/core_ibex/common/ibex_mem_intf_agent/ibex_mem_intf_agent_pkg.sv ${PRJ_DIR}/ibex/dv/uvm/core_ibex/common/irq_agent/irq_if.sv ${PRJ_DIR}/ibex/dv/uvm/core_ibex/common/irq_agent/irq_agent_pkg.sv +${PRJ_DIR}/ibex/dv/uvm/core_ibex/env/core_ibex_instr_monitor_if.sv ${PRJ_DIR}/ibex/dv/uvm/core_ibex/env/core_ibex_dut_probe_if.sv ${PRJ_DIR}/ibex/dv/uvm/core_ibex/env/core_ibex_rvfi_if.sv ${PRJ_DIR}/ibex/dv/uvm/core_ibex/env/core_ibex_csr_if.sv diff --git a/dv/uvm/core_ibex/riscv_dv_extension/testlist.yaml b/dv/uvm/core_ibex/riscv_dv_extension/testlist.yaml index a9cb249e..37d82b6c 100644 --- a/dv/uvm/core_ibex/riscv_dv_extension/testlist.yaml +++ b/dv/uvm/core_ibex/riscv_dv_extension/testlist.yaml @@ -186,6 +186,33 @@ compare_final_value_only: 1 verbose: 1 +- test: riscv_debug_instr_test + description: > + At a high level, this test checks that Ibex can correctly respond after receiving debug + stimulus while every supported instruction is in its decoding stage. + e.g. If the test detects a LUI instruction in the decoding stage, a debug request is + generated and sent into Ibex. We never send a debug request if we see a LUI instruction again, + as we don't want to send in debug requests for every instruction that is executed. + iterations: 25 + gen_test: riscv_rand_instr_test + gen_opts: > + +require_signature_addr=1 + +gen_debug_section=1 + +randomize_csr=1 + +no_csr_instr=0 + +no_ebreak=1 + +no_fence=0 + +no_wfi=0 + +directed_instr_0=riscv_load_store_rand_instr_stream,40 + +directed_instr_1=riscv_load_store_hazard_instr_stream,40 + rtl_test: core_ibex_debug_instr_test + sim_opts: > + +require_signature_addr=1 + +enable_debug_seq=1 + compare_opts: + compare_final_value_only: 1 + verbose: 1 + - test: riscv_debug_wfi_test description: > Assert debug_req while core is in WFI sleep state, should jump to debug mode @@ -384,6 +411,34 @@ compare_opts: compare_final_value_only: 1 +- test: riscv_interrupt_instr_test + description: > + At a high level, this test checks that Ibex can correctly respond after receiving interrupt + stimulus while every supported instruction is in its decoding stage. + e.g. If the test detects a LUI instruction in the decoding stage, an interrupt is + generated and sent into Ibex. We never send an interrupt if we see a LUI instruction again, + as we don't want to send interrupts for every instruction that is executed. + iterations: 25 + gen_test: riscv_rand_instr_test + gen_opts: > + +instr_cnt=6000 + +require_signature_addr=1 + +enable_interrupt=1 + +enable_timer_irq=1 + +randomize_csr=1 + +no_csr_instr=0 + +no_ebreak=1 + +no_fence=0 + +no_wfi=0 + +directed_instr_0=riscv_load_store_rand_instr_stream,40 + +directed_instr_1=riscv_load_store_hazard_instr_stream,40 + rtl_test: core_ibex_interrupt_instr_test + sim_opts: > + +require_signature_addr=1 + +enable_irq_single_seq=1 + compare_opts: + compare_final_value_only: 1 + - test: riscv_interrupt_wfi_test description: > Inject interrupts after a encountering wfi instructions. 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 57c5915a..636fd5e7 100644 --- a/dv/uvm/core_ibex/tb/core_ibex_tb_top.sv +++ b/dv/uvm/core_ibex/tb/core_ibex_tb_top.sv @@ -20,6 +20,9 @@ module core_ibex_tb_top; // DUT probe interface core_ibex_dut_probe_if dut_if(.clk(clk)); + // Instruction monitor interface + core_ibex_instr_monitor_if instr_monitor_if(); + // RVFI interface core_ibex_rvfi_if rvfi_if(.clk(clk)); @@ -93,53 +96,63 @@ module core_ibex_tb_top; ); // Data load/store vif connection - assign data_mem_vif.reset = ~rst_n; + assign data_mem_vif.reset = ~rst_n; // Instruction fetch vif connnection - assign instr_mem_vif.reset = ~rst_n; - assign instr_mem_vif.we = 0; - assign instr_mem_vif.be = 0; - assign instr_mem_vif.wdata = 0; + assign instr_mem_vif.reset = ~rst_n; + assign instr_mem_vif.we = 0; + assign instr_mem_vif.be = 0; + assign instr_mem_vif.wdata = 0; // RVFI interface connections - 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.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; // Irq interface connections - assign irq_vif.reset = ~rst_n; + assign irq_vif.reset = ~rst_n; // Dut_if interface connections - assign dut_if.ecall = dut.u_ibex_core.id_stage_i.controller_i.ecall_insn; - assign dut_if.wfi = dut.u_ibex_core.id_stage_i.controller_i.wfi_insn; - assign dut_if.ebreak = dut.u_ibex_core.id_stage_i.controller_i.ebrk_insn; - assign dut_if.illegal_instr = dut.u_ibex_core.id_stage_i.controller_i.illegal_insn_d; - assign dut_if.dret = dut.u_ibex_core.id_stage_i.controller_i.dret_insn; - assign dut_if.mret = dut.u_ibex_core.id_stage_i.controller_i.mret_insn; - assign dut_if.reset = ~rst_n; - assign dut_if.priv_mode = dut.u_ibex_core.priv_mode_id; + assign dut_if.ecall = dut.u_ibex_core.id_stage_i.controller_i.ecall_insn; + assign dut_if.wfi = dut.u_ibex_core.id_stage_i.controller_i.wfi_insn; + assign dut_if.ebreak = dut.u_ibex_core.id_stage_i.controller_i.ebrk_insn; + assign dut_if.illegal_instr = dut.u_ibex_core.id_stage_i.controller_i.illegal_insn_d; + assign dut_if.dret = dut.u_ibex_core.id_stage_i.controller_i.dret_insn; + assign dut_if.mret = dut.u_ibex_core.id_stage_i.controller_i.mret_insn; + assign dut_if.reset = ~rst_n; + assign dut_if.priv_mode = dut.u_ibex_core.priv_mode_id; + // Instruction monitor connections + assign instr_monitor_if.valid_id = dut.u_ibex_core.id_stage_i.instr_valid_i; + assign instr_monitor_if.err_id = dut.u_ibex_core.id_stage_i.controller_i.instr_fetch_err; + assign instr_monitor_if.is_compressed_id = dut.u_ibex_core.id_stage_i.instr_is_compressed_i; + assign instr_monitor_if.instr_compressed_id = dut.u_ibex_core.id_stage_i.instr_rdata_c_i; + assign instr_monitor_if.instr_id = dut.u_ibex_core.id_stage_i.instr_rdata_i; // CSR interface connections - assign csr_if.csr_access = dut.u_ibex_core.csr_access; - assign csr_if.csr_addr = dut.u_ibex_core.csr_addr; - assign csr_if.csr_wdata = dut.u_ibex_core.csr_wdata; - assign csr_if.csr_rdata = dut.u_ibex_core.csr_rdata; - assign csr_if.csr_op = dut.u_ibex_core.csr_op; + assign csr_if.csr_access = dut.u_ibex_core.csr_access; + assign csr_if.csr_addr = dut.u_ibex_core.csr_addr; + assign csr_if.csr_wdata = dut.u_ibex_core.csr_wdata; + assign csr_if.csr_rdata = dut.u_ibex_core.csr_rdata; + assign csr_if.csr_op = dut.u_ibex_core.csr_op; initial begin uvm_config_db#(virtual clk_if)::set(null, "*", "clk_if", ibex_clk_if); uvm_config_db#(virtual core_ibex_dut_probe_if)::set(null, "*", "dut_if", dut_if); + uvm_config_db#(virtual core_ibex_instr_monitor_if)::set(null, + "*", + "instr_monitor_if", + instr_monitor_if); uvm_config_db#(virtual core_ibex_csr_if)::set(null, "*", "csr_if", csr_if); uvm_config_db#(virtual core_ibex_rvfi_if)::set(null, "*", "rvfi_if", rvfi_if); uvm_config_db#(virtual ibex_mem_intf)::set(null, "*data_if_slave*", "vif", data_mem_vif); 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 94fa6729..6847551b 100644 --- a/dv/uvm/core_ibex/tests/core_ibex_base_test.sv +++ b/dv/uvm/core_ibex/tests/core_ibex_base_test.sv @@ -8,6 +8,7 @@ class core_ibex_base_test extends uvm_test; core_ibex_env_cfg cfg; virtual clk_if clk_vif; virtual core_ibex_dut_probe_if dut_vif; + virtual core_ibex_instr_monitor_if instr_vif; virtual core_ibex_csr_if csr_vif; mem_model_pkg::mem_model mem; core_ibex_vseq vseq; @@ -39,13 +40,18 @@ class core_ibex_base_test extends uvm_test; super.build_phase(phase); $value$plusargs("timeout_in_cycles=%0d", timeout_in_cycles); if (!uvm_config_db#(virtual clk_if)::get(null, "", "clk_if", clk_vif)) begin - `uvm_fatal(get_full_name(), "Cannot get clk_if") + `uvm_fatal(`gfn, "Cannot get clk_if") end if (!uvm_config_db#(virtual core_ibex_dut_probe_if)::get(null, "", "dut_if", dut_vif)) begin - `uvm_fatal(get_full_name(), "Cannot get dut_if") + `uvm_fatal(`gfn, "Cannot get dut_if") + end + if (!uvm_config_db#(virtual core_ibex_instr_monitor_if)::get(null, "", + "instr_monitor_if", + instr_vif)) begin + `uvm_fatal(`gfn, "Cannot get instr_monitor_if") end if (!uvm_config_db#(virtual core_ibex_csr_if)::get(null, "", "csr_if", csr_vif)) begin - `uvm_fatal(get_full_name(), "Cannot get csr_if") + `uvm_fatal(`gfn, "Cannot get csr_if") end env = core_ibex_env::type_id::create("env", this); cfg = core_ibex_env_cfg::type_id::create("cfg", this); diff --git a/dv/uvm/core_ibex/tests/core_ibex_seq_lib.sv b/dv/uvm/core_ibex/tests/core_ibex_seq_lib.sv index 1ec9da37..7aee40c7 100644 --- a/dv/uvm/core_ibex/tests/core_ibex_seq_lib.sv +++ b/dv/uvm/core_ibex/tests/core_ibex_seq_lib.sv @@ -66,56 +66,76 @@ endclass // Interrupt sequences -class irq_raise_seq extends core_base_seq#(irq_seq_item); +class irq_base_seq extends core_base_seq #(irq_seq_item); - `uvm_object_utils(irq_raise_seq) + `uvm_object_utils(irq_base_seq) `uvm_object_new + bit no_nmi; + bit no_fast; virtual task send_req(); irq_seq_item irq; - irq = irq_seq_item::type_id::create($sformatf("irq_raise_single[%0d]", iteration_cnt)); + irq = irq_seq_item::type_id::create($sformatf("irq_seq_item[%0d]", iteration_cnt)); start_item(irq); - `DV_CHECK_RANDOMIZE_WITH_FATAL(irq, num_of_interrupt > 1; irq_nm == ~no_nmi;) + randomize_item(irq); finish_item(irq); get_response(irq); endtask + virtual function void randomize_item(irq_seq_item irq); + `uvm_fatal(`gfn, "This task must be implemented in extended irq sequences") + endfunction + endclass -class irq_raise_single_seq extends core_base_seq#(irq_seq_item); +class irq_raise_seq extends irq_base_seq; + + `uvm_object_utils(irq_raise_seq) + `uvm_object_new + + bit no_nmi; + bit no_fast; + + virtual function void randomize_item(irq_seq_item irq); + `DV_CHECK_RANDOMIZE_WITH_FATAL(irq, num_of_interrupt > 1; + irq_nm == ~no_nmi; + if (no_fast) { + irq_fast == '0; + }) + endfunction + +endclass + +class irq_raise_single_seq extends irq_base_seq; `uvm_object_utils(irq_raise_single_seq) `uvm_object_new - bit no_nmi; - virtual task send_req(); - irq_seq_item irq; - irq = irq_seq_item::type_id::create($sformatf("irq_raise_single[%0d]", iteration_cnt)); - start_item(irq); - `DV_CHECK_RANDOMIZE_WITH_FATAL(irq, num_of_interrupt == 1; irq_nm == ~no_nmi;) - finish_item(irq); - get_response(irq); - endtask + bit no_nmi; + bit no_fast; + + virtual function void randomize_item(irq_seq_item irq); + `DV_CHECK_RANDOMIZE_WITH_FATAL(irq, num_of_interrupt == 1; + irq_nm == ~no_nmi; + if (no_fast) { + irq_fast == '0; + }) + endfunction endclass // Irq sequence to deassert all interrupt lines, since Ibex interrupts are level sensitive -class irq_drop_seq extends core_base_seq#(irq_seq_item); +class irq_drop_seq extends irq_base_seq; `uvm_object_utils(irq_drop_seq) `uvm_object_new // TODO(udinator) - for nested interrupt tests, test scenarios where a random number of interrupts // are dropped - virtual task send_req(); - irq_seq_item irq; - irq = irq_seq_item::type_id::create($sformatf("irq_drop[%0d]", iteration_cnt)); - start_item(irq); + virtual function void randomize_item(irq_seq_item irq); `DV_CHECK_RANDOMIZE_WITH_FATAL(irq, num_of_interrupt == 0;) - finish_item(irq); - get_response(irq); - endtask + endfunction endclass @@ -127,6 +147,8 @@ class debug_seq extends core_base_seq#(irq_seq_item); `uvm_object_utils(debug_seq) `uvm_object_new + int unsigned drop_delay = 75; + virtual task body(); if (!uvm_config_db#(virtual core_ibex_dut_probe_if)::get(null, "", "dut_if", dut_vif)) begin `uvm_fatal(get_full_name(), "Cannot get dut_if") @@ -138,7 +160,7 @@ class debug_seq extends core_base_seq#(irq_seq_item); virtual task send_req(); `uvm_info(get_full_name(), "Sending debug request", UVM_HIGH) dut_vif.dut_cb.debug_req <= 1'b1; - clk_vif.wait_clks(50); + clk_vif.wait_clks(drop_delay); dut_vif.dut_cb.debug_req <= 1'b0; endtask diff --git a/dv/uvm/core_ibex/tests/core_ibex_test_lib.sv b/dv/uvm/core_ibex/tests/core_ibex_test_lib.sv index 7d869823..4da754fb 100644 --- a/dv/uvm/core_ibex/tests/core_ibex_test_lib.sv +++ b/dv/uvm/core_ibex/tests/core_ibex_test_lib.sv @@ -196,11 +196,12 @@ class core_ibex_debug_intr_basic_test extends core_ibex_base_test; endtask virtual task send_irq_stimulus_start(input bit no_nmi, + input bit no_fast, output bit ret_val); bit irq_valid; // send the interrupt - if (cfg.enable_irq_single_seq) vseq.start_irq_raise_single_seq(no_nmi); - else if (cfg.enable_irq_multiple_seq) vseq.start_irq_raise_seq(no_nmi); + if (cfg.enable_irq_single_seq) vseq.start_irq_raise_single_seq(no_nmi, no_fast); + else if (cfg.enable_irq_multiple_seq) vseq.start_irq_raise_seq(no_nmi, no_fast); irq_collected_port.get(irq_txn); irq = {irq_txn.irq_nm, irq_txn.irq_fast, 4'b0, irq_txn.irq_external, 3'b0, irq_txn.irq_timer, 3'b0, irq_txn.irq_software, 3'b0}; @@ -269,9 +270,9 @@ class core_ibex_debug_intr_basic_test extends core_ibex_base_test; wait_ret("mret", 1000); endtask - virtual task send_irq_stimulus(bit no_nmi = 1'b0); + virtual task send_irq_stimulus(bit no_nmi = 1'b0, bit no_fast = 1'b0); bit ret_val; - send_irq_stimulus_start(no_nmi, ret_val); + send_irq_stimulus_start(no_nmi, no_fast, ret_val); if (ret_val) send_irq_stimulus_end(); endtask @@ -364,6 +365,9 @@ class core_ibex_directed_test extends core_ibex_debug_intr_basic_test; `uvm_component_utils(core_ibex_directed_test) `uvm_component_new + instr_t seen_instr[$]; + bit [15:0] seen_compressed_instr[$]; + virtual task send_stimulus(); fork begin @@ -390,6 +394,9 @@ class core_ibex_directed_test extends core_ibex_debug_intr_basic_test; // Wait for core initialization before starting the stimulus check loop - first write // to signature address is guaranteed to be core initialization info wait_for_core_setup(); + // Wait for a little bit to guarantee that the core has started executing
+ // before starting to generate stimulus for the core. + clk_vif.wait_clks(50); // Should be extended by derived classes. // DO NOT use this test class directly. fork @@ -465,6 +472,209 @@ class core_ibex_directed_test extends core_ibex_debug_intr_basic_test; "Incorrect dcsr.prv value!") endfunction + // Check if we have seen the same type of instruction before by comparing the instruction + // currently in the ID stage against the global seen_instr[$] queue. + // If we've seen the same type of instruction before, return 0, otherwise add it to the + // seen_instr[$] queue and return 1. + virtual function bit decode_instr(bit [ibex_mem_intf_agent_pkg::DATA_WIDTH-1:0] instr); + ibex_pkg::opcode_e opcode; + bit [2:0] funct3; + bit [6:0] funct7; + bit [12:0] system_imm; + instr_t instr_fields; + + opcode = instr[6:0]; + funct3 = instr[14:12]; + funct7 = instr[31:25]; + system_imm = instr[31:20]; + + // Now we search seen_instr[$] to check if a same instruction has been seen before. + case (opcode) + OPCODE_LUI, OPCODE_AUIPC, OPCODE_JAL: begin + // these instructions only depend on opcode. + foreach (seen_instr[i]) begin + if (opcode == seen_instr[i].opcode) begin + return 0; + end + end + end + OPCODE_JALR, OPCODE_BRANCH, OPCODE_LOAD, + OPCODE_STORE, OPCODE_MISC_MEM: begin + // these instructions only depend on opcode and funct3 + // to be identified. + foreach (seen_instr[i]) begin + if (opcode == seen_instr[i].opcode && + funct3 == seen_instr[i].funct3) begin + return 0; + end + end + end + OPCODE_OP_IMM: begin + // register-immediate arithmetic instructions are handled separately + // as slli/srli/srai rely on funct7 in addition to opcode/funct3. + foreach (seen_instr[i]) begin + if (opcode == seen_instr[i].opcode && + funct3 == seen_instr[i].funct3) begin + // handle slli/srli/srai instructions. + if (funct3 inside {3'b001, 3'b101}) begin + if (funct7 == seen_instr[i].funct7) begin + return 0; + end + end else begin + return 0; + end + end + end + end + OPCODE_OP: begin + // all register-register arithmetic instructions rely on + // opcode/funct3/funct7 for identification. + foreach (seen_instr[i]) begin + if (opcode == seen_instr[i].opcode && + funct3 == seen_instr[i].funct3 && + funct7 == seen_instr[i].funct7) begin + return 0; + end + end + end + OPCODE_SYSTEM: begin + // explicitly set is_seen to 0 and return on WFI instructions, + // as if we don't interrupt them, every test will timeout. + if (funct3 == 3'b000 && system_imm == 12'h105) begin + return 0; + end else if (funct3 == 3'b000 && system_imm != 12'h001) begin + // raise is_seen if ecall/mret/dret is detected, + // we exclude them for now (this leads to nested traps). + return 0; + end else begin + foreach (seen_instr[i]) begin + if (opcode == seen_instr[i].opcode && + funct3 == seen_instr[i].funct3 && + system_imm == seen_instr[i].system_imm) begin + return 0; + end + end + end + end + default: begin + `uvm_fatal(`gfn, "Illegal instruction detected") + end + endcase + + // We haven't seen this type of instruction before, so add it to seen_instr[$] + // to flag it as 'seen' the next time we decode an instruction. + instr_fields = '{opcode, funct3, funct7, system_imm}; + seen_instr.push_back(instr_fields); + return 1; + + endfunction + + // Similarly to decode_instr(...), this function checks whether we have seen the + // compressed instruction currently in the ID stage before by comparing it to the + // global seen_compressed_instr[$] queue. + // If we have seen it before, it returns 0, otherwise the instruction is added to the + // and it returns 1. + virtual function bit decode_compressed_instr(bit [15:0] instr); + + foreach (seen_compressed_instr[i]) begin + if (instr[1:0] == seen_compressed_instr[i][1:0]) begin + case (instr[1:0]) + 2'b00: begin + if (instr[15:13] == seen_compressed_instr[i][15:13]) begin + return 0; + end + end + 2'b01: begin + if (instr[15:13] == seen_compressed_instr[i][15:13]) begin + case (instr[15:13]) + 3'b000, 3'b001, 3'b010, + 3'b011, 3'b101, 3'b110, 3'b111: begin + return 0; + end + 3'b100: begin + if (instr[11:10] == seen_compressed_instr[i][11:10]) begin + case (instr[11:10]) + 2'b00, 2'b01, 2'b10: begin + return 0; + end + 2'b11: begin + if (instr[12] == seen_compressed_instr[i][12] && + instr[6:5] == seen_compressed_instr[i][6:5]) begin + return 0; + end + end + endcase + end + end + default: begin + `uvm_fatal(`gfn, "Invalid C1 compressed instruction") + end + endcase + end + end + 2'b10: begin + if (instr[15:13] == seen_compressed_instr[i][15:13]) begin + case (instr[15:13]) + 3'b000, 3'b010, 3'b110: begin + return 0; + end + 3'b100: begin + if (instr[12] == seen_compressed_instr[i][12]) begin + return 0; + end + end + default: begin + `uvm_fatal(`gfn, "Illegal C2 compressed instruction") + end + endcase + end + end + default: begin + `uvm_fatal(`gfn, "Instruction is not compressed") + end + endcase + end + end + + // If we get here we have not seen the current instruction before, + // so add it to seen_compressed_instr[$]. + seen_compressed_instr.push_back(instr); + return 1'b1; + + endfunction + +endclass + +// A directed interrupt test that sends interrupt stimulus into the core +// after seeing every unique (and supported) RISC-V instruction in the core's +// Instruction Decode stage. +class core_ibex_interrupt_instr_test extends core_ibex_directed_test; + + `uvm_component_utils(core_ibex_interrupt_instr_test) + `uvm_component_new + + virtual task check_stimulus(); + vseq.irq_raise_single_seq_h.max_delay = 0; + vseq.irq_raise_single_seq_h.max_interval = 0; + forever begin + // hold until we see a valid instruction in the ID stage of the pipeline. + wait (instr_vif.valid_id && !(instr_vif.err_id || dut_vif.illegal_instr)); + + // We don't want to send fast interrupts, as due to the random setup of MIE, + // there's no guarantee that the interrupt will actually be taken. + if (instr_vif.is_compressed_id) begin + if (decode_compressed_instr(instr_vif.instr_compressed_id)) begin + send_irq_stimulus(.no_fast(1'b1)); + end + end else begin + if (decode_instr(instr_vif.instr_id)) begin + send_irq_stimulus(.no_fast(1'b1)); + end + end + clk_vif.wait_clks(1); + end + endtask + endclass // Interrupt WFI test class @@ -557,7 +767,7 @@ class core_ibex_debug_in_irq_test extends core_ibex_directed_test; // then finish interrupt handling routine bit valid_irq; forever begin - send_irq_stimulus_start(1'b0, valid_irq); + send_irq_stimulus_start(1'b0, 1'b0, valid_irq); if (valid_irq) begin fork begin @@ -586,7 +796,7 @@ class core_ibex_nested_irq_test extends core_ibex_directed_test; bit valid_nested_irq; int unsigned initial_irq_delay; forever begin - send_irq_stimulus_start(1'b1, valid_irq); + send_irq_stimulus_start(1'b1, 1'b0, valid_irq); if (valid_irq) begin initial_irq_delay = vseq.irq_raise_seq_h.max_delay; vseq.irq_raise_seq_h.max_delay = 0; @@ -606,6 +816,42 @@ class core_ibex_nested_irq_test extends core_ibex_directed_test; endclass +// A directed debug test that sends debug stimulus into the core +// after seeing every unique (and supported) RISC-V instruction in the core's +// Instruction Decode stage. +class core_ibex_debug_instr_test extends core_ibex_directed_test; + + `uvm_component_utils(core_ibex_debug_instr_test) + `uvm_component_new + + virtual task check_stimulus(); + vseq.debug_seq_single_h.max_delay = 0; + vseq.debug_seq_single_h.max_interval = 0; + forever begin + // hold until we see a valid instruction in the ID stage of the pipeline. + wait (instr_vif.valid_id && !(instr_vif.err_id || dut_vif.illegal_instr)); + + // We don't want to send fast interrupts, as due to the random setup of MIE, + // there's no guarantee that the interrupt will actually be taken. + if (instr_vif.is_compressed_id) begin + if (decode_compressed_instr(instr_vif.instr_compressed_id)) begin + send_debug_stimulus(init_operating_mode, + $sformatf("Did not jump into debug mode after instruction[0x%0x]", + instr_vif.instr_compressed_id)); + end + end else begin + if (decode_instr(instr_vif.instr_id)) begin + send_debug_stimulus(init_operating_mode, + $sformatf("Did not jump into debug mode after instruction[0x%0x]", + instr_vif.instr_id)); + end + end + clk_vif.wait_clks(1); + end + endtask + +endclass + // Debug WFI test class class core_ibex_debug_wfi_test extends core_ibex_directed_test; diff --git a/dv/uvm/core_ibex/tests/core_ibex_test_pkg.sv b/dv/uvm/core_ibex/tests/core_ibex_test_pkg.sv index a7b98034..1cd992b8 100644 --- a/dv/uvm/core_ibex/tests/core_ibex_test_pkg.sv +++ b/dv/uvm/core_ibex/tests/core_ibex_test_pkg.sv @@ -14,6 +14,14 @@ package core_ibex_test_pkg; import riscv_signature_pkg::*; import ibex_pkg::*; + typedef struct { + ibex_pkg::opcode_e opcode; + bit [2:0] funct3; + bit [6:0] funct7; + // 12-bit immediate, used only for SYSTEM instructions + bit [11:0] system_imm; + } instr_t; + `include "core_ibex_report_server.sv" `include "core_ibex_seq_lib.sv" `include "core_ibex_vseq.sv" diff --git a/dv/uvm/core_ibex/tests/core_ibex_vseq.sv b/dv/uvm/core_ibex/tests/core_ibex_vseq.sv index 951bf3d8..2f46c18c 100644 --- a/dv/uvm/core_ibex/tests/core_ibex_vseq.sv +++ b/dv/uvm/core_ibex/tests/core_ibex_vseq.sv @@ -93,13 +93,15 @@ class core_ibex_vseq extends uvm_sequence; debug_seq_single_h.start(null); endtask - virtual task start_irq_raise_single_seq(bit no_nmi = 1'b0); + virtual task start_irq_raise_single_seq(bit no_nmi = 1'b0, bit no_fast = 1'b0); irq_raise_single_seq_h.no_nmi = no_nmi; + irq_raise_single_seq_h.no_fast = no_fast; irq_raise_single_seq_h.start(p_sequencer.irq_seqr); endtask - virtual task start_irq_raise_seq(bit no_nmi = 1'b0); + virtual task start_irq_raise_seq(bit no_nmi = 1'b0, bit no_fast = 1'b0); irq_raise_seq_h.no_nmi = no_nmi; + irq_raise_seq_h.no_fast = no_fast; irq_raise_seq_h.start(p_sequencer.irq_seqr); endtask