diff --git a/dv/uvm/riscv_dv_extension/ibex_log_to_trace_csv.py b/dv/uvm/riscv_dv_extension/ibex_log_to_trace_csv.py index 5377300c..d6c7fac8 100644 --- a/dv/uvm/riscv_dv_extension/ibex_log_to_trace_csv.py +++ b/dv/uvm/riscv_dv_extension/ibex_log_to_trace_csv.py @@ -81,9 +81,9 @@ def check_ibex_uvm_log(uvm_log, core_name, test_name, report, write=True): fd = sys.stdout fd.write("%s uvm log : %s\n" % (core_name, uvm_log)) if pass_cnt == 1: - fd.write("%s : PASSED\n" % test_name) + fd.write("%s : [PASSED]\n\n" % test_name) elif fail_cnt == 1: - fd.write("%s : FAILED\n" % test_name) + fd.write("%s : [FAILED]\n\n" % test_name) if report: fd.close() diff --git a/dv/uvm/riscv_dv_extension/testlist.yaml b/dv/uvm/riscv_dv_extension/testlist.yaml index 7d9c2a17..d21b79e2 100644 --- a/dv/uvm/riscv_dv_extension/testlist.yaml +++ b/dv/uvm/riscv_dv_extension/testlist.yaml @@ -116,7 +116,7 @@ +no_csr_instr=1 +no_fence=1 +num_of_sub_program=0 - rtl_test: core_ibex_debug_intr_test + rtl_test: core_ibex_debug_intr_basic_test sim_opts: > +require_signature_addr=1 +max_interval=1000 @@ -135,14 +135,13 @@ +instr_cnt=6000 +no_csr_instr=1 +no_fence=1 - rtl_test: core_ibex_debug_intr_test + rtl_test: core_ibex_debug_intr_basic_test iterations: 5 sim_opts: > +max_interval=250 +enable_debug_stress_seq=1 +require_signature_addr=1 compare_opts: - compare_final_value_only: 1 verbose: 1 - test: riscv_debug_branch_jump_test @@ -158,11 +157,12 @@ +instr_cnt=6000 +no_csr_instr=1 +no_fence=1 - +num_of_sub_program=5 - +num_debug_sub_program=5 - rtl_test: core_ibex_debug_intr_test + +num_of_sub_program=0 + +num_debug_sub_program=3 + rtl_test: core_ibex_debug_intr_basic_test sim_opts: > +require_signature_addr=1 + +max_interval=2000 +enable_debug_stress_seq=1 compare_opts: compare_final_value_only: 1 @@ -189,6 +189,75 @@ compare_final_value_only: 1 verbose: 1 +- test: riscv_dret_test + description: > + Dret instructions will be inserted into M-mode code, ibex should treat these + like illegal instructions. + iterations: 5 + gen_test: riscv_rand_instr_test + gen_opts: > + +require_signature_addr=1 + +no_dret=0 + +instr_cnt=6000 + rtl_test: core_ibex_dret_test + sim_opts: > + +require_signature_addr=1 + +- test: riscv_ebreak_test + description: > + Ebreak instructions will be inserted into the M mode code, ibex should handle them normally. + iterations: 5 + gen_test: riscv_rand_instr_test + gen_opts: > + +no_ebreak=0 + +instr_cnt=6000 + rtl_test: core_ibex_base_test + +- test: riscv_debug_ebreak_test + description: > + A directed ebreak sequence will be inserted into the debug rom, upon encountering it, + ibex should jump back to the beginning of debug mode. The sequence is designed to avoid an + infinite loop. + iterations: 5 + gen_test: riscv_rand_instr_test + gen_opts: > + +require_signature_addr=1 + +gen_debug_section=1 + +enable_ebreak_in_debug_rom=1 + +no_csr_instr=1 + +no_fence=1 + +no_wfi=1 + +no_ebreak=0 + +instr_cnt=6000 + rtl_test: core_ibex_debug_ebreak_test + sim_opts: > + +require_signature_addr=1 + +enable_debug_single_seq=1 + compare_opts: + compare_final_value_only: 1 + +- test: riscv_debug_ebreakm_test + description: > + dcsr.ebreakm will be set at the beginning of the test upon the first entry into the debug rom. + From then on, every ebreak instruction should cause debug mode to be entered. + iterations: 5 + gen_test: riscv_rand_instr_test + gen_opts: > + +require_signature_addr=1 + +gen_debug_section=1 + +set_dcsr_ebreak=1 + +no_ebreak=0 + +no_csr_instr=1 + +no_fence=1 + +no_wfi=1 + +instr_cnt=6000 + rtl_test: core_ibex_debug_ebreakm_test + sim_opts: > + +require_signature_addr=1 + +enable_debug_single_seq=1 + compare_opts: + compare_final_value_only: 1 + - test: riscv_interrupt_test description: > Random instruction test with complete interrupt handling @@ -196,7 +265,7 @@ gen_test: riscv_rand_instr_test gen_opts: > +require_signature_addr=1 - rtl_test: core_ibex_debug_intr_test + rtl_test: core_ibex_debug_intr_basic_test sim_opts: > +require_signature_addr=1 +enable_irq_stress_seq=1 diff --git a/dv/uvm/sim.py b/dv/uvm/sim.py index 44558c5b..02c501b5 100644 --- a/dv/uvm/sim.py +++ b/dv/uvm/sim.py @@ -166,8 +166,8 @@ def compare(test_list, iss, output_dir, verbose): print("Comparing %s/DUT sim result : %s" % (iss, elf)) run_cmd(("echo 'Test binary: %s' >> %s" % (elf, report))) uvm_log = ("%s/rtl_sim/%s.%d/sim.log" % (output_dir, test['test'], i)) - rtl_log = ("%s/rtl_sim/%s.%d/trace_core_00_0.log" % (output_dir, test['test'], i)) - rtl_csv = ("%s/rtl_sim/%s.%d/trace_core_00_0.csv" % (output_dir, test['test'], i)) + rtl_log = ("%s/rtl_sim/%s.%d/trace_core_00000000.log" % (output_dir, test['test'], i)) + rtl_csv = ("%s/rtl_sim/%s.%d/trace_core_00000000.csv" % (output_dir, test['test'], i)) test_name = "%s.%d" % (test['test'], i) if 'no_post_compare' in test and test['no_post_compare'] == 1: check_ibex_uvm_log(uvm_log, "ibex", test_name, report) diff --git a/dv/uvm/tests/core_ibex_base_test.sv b/dv/uvm/tests/core_ibex_base_test.sv index 75d5d584..54cd2793 100644 --- a/dv/uvm/tests/core_ibex_base_test.sv +++ b/dv/uvm/tests/core_ibex_base_test.sv @@ -12,12 +12,12 @@ class core_ibex_base_test extends uvm_test; core_ibex_vseq vseq; irq_seq irq_seq_h; int unsigned timeout_in_cycles = 3000000; - // If no signature_addr handshake functionality is desired between the - // testbench and the generated code, the test will wait for the specified - // number of cycles before starting stimulus sequences (irq and debug) + // If no signature_addr handshake functionality is desired between the testbench and the generated + // code, the test will wait for the specifield number of cycles before starting stimulus + // sequences (irq and debug) int unsigned stimulus_delay = 800; - bit[ibex_mem_intf_agent_pkg::DATA_WIDTH] signature_data_q[$]; - bit[ibex_mem_intf_agent_pkg::DATA_WIDTH] signature_data; + bit[ibex_mem_intf_agent_pkg::DATA_WIDTH-1:0] signature_data_q[$]; + bit[ibex_mem_intf_agent_pkg::DATA_WIDTH-1:0] signature_data; uvm_tlm_analysis_fifo #(ibex_mem_intf_seq_item) item_collected_port; `uvm_component_utils(core_ibex_base_test) @@ -120,23 +120,20 @@ class core_ibex_base_test extends uvm_test; input signature_type_t ref_type); ibex_mem_intf_seq_item mem_txn; forever begin - // The first write to this address is guaranteed to contain the - // signature type in bits [7:0] + // The first write to this address is guaranteed to contain the signature type in bits [7:0] item_collected_port.get(mem_txn); if (mem_txn.addr == ref_addr && mem_txn.data[7:0] === ref_type && mem_txn.read_write == WRITE) begin signature_data = mem_txn.data; case (ref_type) - // The very first write to the signature address in every test is - // guaranteed to be a write of CORE_STATUS, indicating the - // INITIALIZED state + // The very first write to the signature address in every test is guaranteed to be a write + // of CORE_STATUS, indicating the INITIALIZED state CORE_STATUS: begin signature_data_q.push_back(signature_data >> 8); end TEST_RESULT: begin signature_data_q.push_back(signature_data >> 8); end - // The next 32 writes to the address are guaranteed to be a dump of - // all GPRs + // The next 32 writes to the address are guaranteed to be a dump of all GPRs WRITE_GPR: begin for(int i = 0; i < 32; i++) begin do begin @@ -145,8 +142,7 @@ class core_ibex_base_test extends uvm_test; signature_data_q.push_back(mem_txn.data); end end - // The next write to this address is guaranteed to be the data held - // in the CSR + // The next write to this address is guaranteed to be the data held in the CSR WRITE_CSR: begin signature_data_q.push_back(signature_data >> 8); do begin @@ -163,10 +159,12 @@ class core_ibex_base_test extends uvm_test; end endtask - // API of various tasks wrapping wait_for_mem_txn, for various common - // functionalities that might be needed for verification purposes. + // API of various tasks wrapping wait_for_mem_txn, for various common functionalities that + // might be needed for verification purposes. // Will be expanded as needed. + // Gets the next CORE_STATUS signature write and compares it against the provided core_status + // type, throws uvm_error on mismatch virtual task check_next_core_status(core_status_t core_status, error_msg=""); wait_for_mem_txn(cfg.signature_addr, CORE_STATUS); signature_data = signature_data_q.pop_front(); @@ -175,6 +173,17 @@ class core_ibex_base_test extends uvm_test; end endtask + // Waits for a write to the address of the specified CSR and retrieves the csr data + virtual task wait_for_csr_write(csr_num_e csr); + bit [11:0] csr_addr; + do begin + wait_for_mem_txn(cfg.signature_addr, WRITE_CSR); + csr_addr = signature_data_q.pop_front(); + signature_data = signature_data_q.pop_front(); + end while (csr_addr != csr); + endtask + + // Waits until the next time the given core_status is written to the signature address virtual task wait_for_core_status(core_status_t core_status); do begin wait_for_mem_txn(cfg.signature_addr, CORE_STATUS); diff --git a/dv/uvm/tests/core_ibex_test_lib.sv b/dv/uvm/tests/core_ibex_test_lib.sv index 34bd049c..c306e5b2 100644 --- a/dv/uvm/tests/core_ibex_test_lib.sv +++ b/dv/uvm/tests/core_ibex_test_lib.sv @@ -32,9 +32,9 @@ class core_ibex_csr_test extends core_ibex_base_test; endclass // Debug test class -class core_ibex_debug_intr_test extends core_ibex_base_test; +class core_ibex_debug_intr_basic_test extends core_ibex_base_test; - `uvm_component_utils(core_ibex_debug_intr_test) + `uvm_component_utils(core_ibex_debug_intr_basic_test) `uvm_component_new virtual task send_stimulus(); @@ -46,8 +46,8 @@ class core_ibex_debug_intr_test extends core_ibex_base_test; if (cfg.require_signature_addr) begin wait_for_core_status(INITIALIZED); end else begin - // If no signature_addr functionality is desired, then the test will - // simply wait for an adequate number of cycles + // If no signature_addr functionality is desired, then the test will simply wait for an + // adequate number of cycles clk_vif.wait_clks(stimulus_delay); end fork @@ -68,10 +68,10 @@ class core_ibex_debug_intr_test extends core_ibex_base_test; endclass -// Debug WFI test class -class core_ibex_debug_wfi_test extends core_ibex_base_test; +// Base class for directed debug and irq test scenarios +class core_ibex_directed_test extends core_ibex_base_test; - `uvm_component_utils(core_ibex_debug_wfi_test) + `uvm_component_utils(core_ibex_directed_test) `uvm_component_new virtual task send_stimulus(); @@ -95,30 +95,163 @@ class core_ibex_debug_wfi_test extends core_ibex_base_test; end join_none end else begin - // Wait for core initialization before starting the wfi stimulus - // loop - first write to signature address is guaranteed to be core - // initialization info + // Wait for core initialization before starting the stimulus check loop - first write + // to signature address is guaranteed to be core initialization info check_next_core_status(INITIALIZED, "Core initialization handshake failure"); - // TODO(udi) - need to check that no other instruction fetches occur - // after the WFI is detected, and before any stimulus is sent to the - // core - forever begin - wait (dut_vif.wfi === 1'b1); - clk_vif.wait_clks($urandom_range(100)); - vseq.start_debug_single_seq(); - // After assserting this signal, core should wake up and jump into - // debug mode from WFI state - next handshake should - // be a notification that the core is now in debug mode - check_next_core_status(IN_DEBUG_MODE, "Core did not jump into debug mode from WFI state"); - // We don't want to trigger debug stimulus for any WFI - // instructions encountered inside the debug rom - those should - // act as NOP instructions - so we wait until hitting the end of - // the debug rom - wait (dut_vif.dret === 1'b1); - end + // Should be extended by derived classes. + // DO NOT use this test class directly. + check_stimulus(); end end join_none endtask + virtual task check_stimulus(); + `uvm_fatal(`gfn, "Base class task should not be used") + endtask + + //------------------------------------------------------ + // Checker functions/tasks that might be commonly used + //------------------------------------------------------ + + // compares dcsr.ebreak against the privilege mode encoded in dcsr.prv + virtual function check_dcsr_ebreak(); + // dcsr.prv is the bottom two bits. + case (signature_data[1:0]) + 2'b11: begin + `DV_CHECK_EQ_FATAL(signature_data[15], 1'b1, "dcsr.ebreakm is not set") + end + 2'b01: begin + `DV_CHECK_EQ_FATAL(signature_data[13], 1'b1, "dcsr.ebreaks is not set") + end + 2'b00: begin + `DV_CHECK_EQ_FATAL(signature_data[12], 1'b1, "dcsr.ebreaku is not set") + end + default: begin + `uvm_fatal(`gfn, "dcsr.prv is an unsupported privilege mode") + end + endcase + endfunction + + virtual function check_dcsr_cause(dbg_cause_e cause); + `DV_CHECK_EQ_FATAL(cause, signature_data[8:6], "dcsr.cause has been incorrectly updated") + endfunction + +endclass + +// Debug WFI test class +class core_ibex_debug_wfi_test extends core_ibex_directed_test; + + `uvm_component_utils(core_ibex_debug_wfi_test) + `uvm_component_new + + virtual task check_stimulus(); + // TODO(udi) - need to check that no other instruction fetches occur after after the WFI + // is detected, and before any stimulus is sent to the core + forever begin + wait (dut_vif.wfi === 1'b1); + clk_vif.wait_clks($urandom_range(100)); + vseq.start_debug_single_seq(); + // After assserting this signal, core should wake up and jump into debug mode from WFI state + // - next handshake should be a notification that the core is now in debug mode + check_next_core_status(IN_DEBUG_MODE, "Core did not jump into debug mode from WFI state"); + // We don't want to trigger debug stimulus for any WFI instructions encountered inside the + // debug rom - those should act as NOP instructions - so we wait until hitting the end of the + // debug rom. + // We also want to check that dcsr.cause has been set correctly + wait_for_csr_write(CSR_DCSR); + check_dcsr_cause(DBG_CAUSE_HALTREQ); + wait (dut_vif.dret === 1'b1); + end + endtask + +endclass + +// DRET test class +class core_ibex_dret_test extends core_ibex_directed_test; + + `uvm_component_utils(core_ibex_dret_test) + `uvm_component_new + + virtual task check_stimulus(); + forever begin + wait (dut_vif.dret === 1'b1); + // After hitting a dret, the core will jump to the vectored trap handler, which sends a + // handshake write to the bench + check_next_core_status(HANDLING_EXCEPTION, "Core did not jump to vectored exception handler"); + // The core will receive an illegal instruction handshake after jumping from the vectored trap + // handler to the illegal instruction exception handler + check_next_core_status(ILLEGAL_INSTR_EXCEPTION, "Core did not treat dret like illegal instruction"); + end + endtask + +endclass + +// Normal debug ebreak test class +class core_ibex_debug_ebreak_test extends core_ibex_directed_test; + + `uvm_component_utils(core_ibex_debug_ebreak_test) + `uvm_component_new + + bit[ibex_mem_intf_agent_pkg::DATA_WIDTH-1:0] dpc; + bit[ibex_mem_intf_agent_pkg::DATA_WIDTH-1:0] dcsr; + + virtual task check_stimulus(); + forever begin + wait (dut_vif.ebreak === 1'b1); + check_next_core_status(HANDLING_EXCEPTION, "Core did not jump to exception handler"); + check_next_core_status(EBREAK_EXCEPTION, "Core did not jump from exception handler to ebreak handler"); + wait (dut_vif.mret === 1'b1); + // Want to wait until after the ebreak handler has finished to send debug stimulus, to avoid + // nested trap scenarios + clk_vif.wait_clks($urandom_range(5, 11)); + vseq.start_debug_single_seq(); + // capture the first write of dcsr + wait_for_csr_write(CSR_DCSR); + dcsr = signature_data; + // We also want to check that dcsr.cause has been set correctly + check_dcsr_cause(DBG_CAUSE_HALTREQ); + // capture the first write of dpc + wait_for_csr_write(CSR_DPC); + dpc = signature_data; + check_next_core_status(IN_DEBUG_MODE, "Core did not properly jump into debug mode"); + wait (dut_vif.ebreak === 1'b1); + // compare the second writes of dcsr and dpc against the captured values + wait_for_csr_write(CSR_DCSR); + `DV_CHECK_EQ_FATAL(dcsr, signature_data, "ebreak inside the debug rom has changed the value of DCSR") + wait_for_csr_write(CSR_DPC); + `DV_CHECK_EQ_FATAL(dpc, signature_data, "ebreak inside the debug rom has changed the value of DPC") + wait (dut_vif.dret === 1'b1); + end + endtask + +endclass + +// Debug ebreak test with dcsr.ebreak(m/s/u) set +class core_ibex_debug_ebreakm_test extends core_ibex_directed_test; + + `uvm_component_utils(core_ibex_debug_ebreakm_test) + `uvm_component_new + + virtual task check_stimulus(); + // send a single debug request after core initialization to configure dcsr + vseq.start_debug_single_seq(); + check_next_core_status(IN_DEBUG_MODE, "Core did not enter debug mode after debug_req stimulus"); + // Read dcsr and verify the appropriate ebreak(m/s/u) bit has been set based on the prv field, + // as well as the cause field + wait_for_csr_write(CSR_DCSR); + check_dcsr_ebreak(); + check_dcsr_cause(DBG_CAUSE_HALTREQ); + wait (dut_vif.dret === 1'b1); + forever begin + wait (dut_vif.ebreak === 1'b1); + check_next_core_status(IN_DEBUG_MODE, "Core did not enter debug mode after execution of ebreak"); + // Read dcsr and verify the appropriate ebreak(m/s/u) bit has been set based on the prv field + wait_for_csr_write(CSR_DCSR); + check_dcsr_ebreak(); + check_dcsr_cause(DBG_CAUSE_EBREAK); + wait (dut_vif.dret === 1'b1); + end + endtask + endclass diff --git a/dv/uvm/tests/core_ibex_test_pkg.sv b/dv/uvm/tests/core_ibex_test_pkg.sv index b6a8d4f0..a7b98034 100644 --- a/dv/uvm/tests/core_ibex_test_pkg.sv +++ b/dv/uvm/tests/core_ibex_test_pkg.sv @@ -12,6 +12,7 @@ package core_ibex_test_pkg; import ibex_mem_intf_agent_pkg::*; import irq_agent_pkg::*; import riscv_signature_pkg::*; + import ibex_pkg::*; `include "core_ibex_report_server.sv" `include "core_ibex_seq_lib.sv" diff --git a/dv/uvm/tests/core_ibex_vseq.sv b/dv/uvm/tests/core_ibex_vseq.sv index 9d22c706..53ccc750 100644 --- a/dv/uvm/tests/core_ibex_vseq.sv +++ b/dv/uvm/tests/core_ibex_vseq.sv @@ -72,8 +72,8 @@ class core_ibex_vseq extends uvm_sequence; end endtask - // Helper tasks to allow the test fine grained control to start sequences - // through the vseq - necessary for testing directed stimulus scenarios + // Helper tasks to allow the test fine grained control to start sequences through the vseq + // - necessary for testing directed stimulus scenarios virtual task start_debug_stress_seq(); debug_seq_stress_h.start(null); endtask