Added dret and ebreak tests (#281)

This commit is contained in:
udinator 2019-09-04 16:14:41 -07:00 committed by GitHub
parent d14312c3cc
commit e9c2b2ecb3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 268 additions and 56 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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