Skip to content

Commit

Permalink
Merge pull request openhwgroup#2280 from XavierAubert/cv32e40p/dev_dd…
Browse files Browse the repository at this point in the history
…_W45a

CV32E40P corev-dv debug gen fixes & HWLoop coverage
  • Loading branch information
MikeOpenHWGroup authored Nov 8, 2023
2 parents e8ca541 + 7c930d6 commit f70c820
Show file tree
Hide file tree
Showing 23 changed files with 2,419 additions and 30 deletions.
40 changes: 35 additions & 5 deletions cv32e40p/env/corev-dv/cv32e40p_asm_program_gen.sv
Original file line number Diff line number Diff line change
Expand Up @@ -550,6 +550,9 @@ class cv32e40p_asm_program_gen extends corev_asm_program_gen;

// Override Illegal instruction handler - gen_illegal_instr_handler
// Replace pop_gpr_from_kernel_stack with pop_regfile_from_kernel_stack
// With RV32X enabled, check for illegal instr on the last instr of hwloop
// If true, then set MEPC to first instr of hwloop instead of simply
// incrementing by 4.
virtual function void gen_illegal_instr_handler(int hart);
string instr[$];
cv32e40p_instr_gen_config corev_cfg;
Expand All @@ -558,11 +561,38 @@ class cv32e40p_asm_program_gen extends corev_asm_program_gen;

gen_signature_handshake(instr, CORE_STATUS, ILLEGAL_INSTR_EXCEPTION);
gen_signature_handshake(.instr(instr), .signature_type(WRITE_CSR), .csr(MCAUSE));
instr = {instr,
$sformatf("csrr x%0d, 0x%0x", cfg.gpr[0], MEPC),
$sformatf("addi x%0d, x%0d, 4", cfg.gpr[0], cfg.gpr[0]),
$sformatf("csrw 0x%0x, x%0d", MEPC, cfg.gpr[0])
};
if (riscv_instr_pkg::RV32X inside {riscv_instr_pkg::supported_isa}) begin
instr = {instr,
$sformatf("csrr x%0d, 0x%0x", cfg.gpr[0], LPCOUNT1),
$sformatf("li x%0d, 2", cfg.gpr[1]),
$sformatf("bge x%0d, x%0d, 1f", cfg.gpr[0], cfg.gpr[1]),
$sformatf("2: csrr x%0d, 0x%0x", cfg.gpr[0], LPCOUNT0),
$sformatf("li x%0d, 2", cfg.gpr[1]),
$sformatf("bge x%0d, x%0d, 3f", cfg.gpr[0], cfg.gpr[1]),
$sformatf("beqz x0, 4f"),
$sformatf("1: csrr x%0d, 0x%0x", cfg.gpr[0], MEPC),
$sformatf("csrr x%0d, 0x%0x", cfg.gpr[1], LPEND1),
$sformatf("addi x%0d, x%0d, -4", cfg.gpr[1], cfg.gpr[1]),
$sformatf("bne x%0d, x%0d, 2b", cfg.gpr[0], cfg.gpr[1]),
$sformatf("csrr x%0d, 0x%0x", cfg.gpr[0], LPSTART1),
$sformatf("beqz x0, 5f"),
$sformatf("3: csrr x%0d, 0x%0x", cfg.gpr[0], MEPC),
$sformatf("csrr x%0d, 0x%0x", cfg.gpr[1], LPEND0),
$sformatf("addi x%0d, x%0d, -4", cfg.gpr[1], cfg.gpr[1]),
$sformatf("bne x%0d, x%0d, 4f", cfg.gpr[0], cfg.gpr[1]),
$sformatf("csrr x%0d, 0x%0x", cfg.gpr[0], LPSTART0),
$sformatf("beqz x0, 5f"),
$sformatf("4: csrr x%0d, 0x%0x", cfg.gpr[0], MEPC),
$sformatf("addi x%0d, x%0d, 4", cfg.gpr[0], cfg.gpr[0]),
$sformatf("5: csrw 0x%0x, x%0d", MEPC, cfg.gpr[0])
};
end else begin
instr = {instr,
$sformatf("csrr x%0d, 0x%0x", cfg.gpr[0], MEPC),
$sformatf("addi x%0d, x%0d, 4", cfg.gpr[0], cfg.gpr[0]),
$sformatf("csrw 0x%0x, x%0d", MEPC, cfg.gpr[0])
};
end
// Replace pop_gpr_from_kernel_stack with pop_regfile_from_kernel_stack
//pop_gpr_from_kernel_stack(MSTATUS, MSCRATCH, cfg.mstatus_mprv, cfg.sp, cfg.tp, instr);
pop_regfile_from_kernel_stack(MSTATUS, MSCRATCH, cfg.mstatus_mprv, cfg.sp, cfg.tp, instr, corev_cfg);
Expand Down
188 changes: 181 additions & 7 deletions cv32e40p/env/corev-dv/cv32e40p_debug_rom_gen.sv
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
//
class cv32e40p_debug_rom_gen extends riscv_debug_rom_gen;
string debug_dret[$];
cv32e40p_instr_gen_config cfg_corev;

`uvm_object_utils(cv32e40p_debug_rom_gen)

Expand All @@ -27,18 +28,17 @@ class cv32e40p_debug_rom_gen extends riscv_debug_rom_gen;

virtual function void gen_program();
string sub_program_name[$] = {};
cv32e40p_instr_gen_config cfg_corev;
privileged_reg_t status = MSTATUS;

if(!uvm_config_db#(cv32e40p_instr_gen_config)::get(null,get_full_name(),"cv32e40p_instr_cfg", cfg_corev)) begin
`uvm_fatal(get_full_name(), "Cannot get cv32e40p_instr_gen_config")
end

// CORE-V Addition
// Insert section info so linker can place
// debug code at the correct adress
instr_stream.push_back(".section .debugger, \"ax\"");

// CORE-V Addition
// Cast CORE-V derived handle to enable fetching core-v config fields
`DV_CHECK($cast(cfg_corev, cfg))

// Randomly add a WFI at start of ddebug rom
// This will be treaed as a NOP always, but added here to close instructon
// combination coverage (i.e. ebreak->wfi)
Expand Down Expand Up @@ -79,6 +79,13 @@ class cv32e40p_debug_rom_gen extends riscv_debug_rom_gen;
// having to execute unnecessary push/pop of GPRs on the stack ever
// time a debug request is sent
gen_signature_handshake(debug_main, CORE_STATUS, IN_DEBUG_MODE);
if (cfg_corev.setup_debug_trigger_on_addr_match) begin
// Setup tdata1 and tdata2 for trigger breakpoint
if (!cfg.enable_debug_single_step)
gen_dbg_trigger_setup_with_addr_match(1); //use_dscratch0=1, num of trigger iterations > 1 allowed
else
gen_dbg_trigger_setup_with_addr_match(0); //use_dscratch0=0, num of trigger iterations = 1
end
if (cfg.enable_ebreak_in_debug_rom) begin
// send dpc and dcsr to testbench, as this handshake will be
// executed twice due to the ebreak loop, there should be no change
Expand All @@ -93,7 +100,11 @@ class cv32e40p_debug_rom_gen extends riscv_debug_rom_gen;
gen_dcsr_ebreak();
end
if (cfg.enable_debug_single_step) begin
gen_single_step_logic();
if ((cfg_corev.debug_trigger_before_single_step & cfg_corev.setup_debug_trigger_on_addr_match) ||
(cfg_corev.debug_ebreak_before_single_step & cfg_corev.set_dcsr_ebreak))
gen_single_step_logic_if_trigger_ebreak();
else
gen_single_step_logic();
end
gen_dpc_update();
// write DCSR to the testbench for any analysis
Expand Down Expand Up @@ -167,6 +178,169 @@ class cv32e40p_debug_rom_gen extends riscv_debug_rom_gen;
// original section
instr_stream.push_back(".section text");
endfunction : gen_debug_exception_handler


// Function: gen_dbg_trigger_setup_with_addr_match
// Inputs int use_dscratch0, dscratch0 is used to store number of
// iterations and overlaps its usage for single step routine as well
// So this input is to differentiate code generation for different
// scenrios with or without single step overlap in the tests
//
// Description: Setup trigger breakpoint setup with instruction address
// match. Uses random config item trigger_addr_offset to set trigger
// address adding this random offset to depc value
// Use config trigger_iterations with use_dscratch0=1 to setup trigger
// multiple times in a single test.
virtual function void gen_dbg_trigger_setup_with_addr_match(int use_dscratch0=0);

if(use_dscratch0) begin
str = {$sformatf("csrw 0x%0x, x%0d", DSCRATCH1, cfg.scratch_reg),
// Only un-set tdata1.execute if it is 1 and the iterations counter
// is at 0 (has finished expected iterations)
$sformatf("csrr x%0d, 0x%0x", cfg.scratch_reg, TDATA1),
$sformatf("andi x%0d, x%0d, 4", cfg.scratch_reg, cfg.scratch_reg),
// If tdata1.execute is 0, set to 1 and set the counter
$sformatf("beqz x%0d, 1f", cfg.scratch_reg),
// Check DCSR.cause, if cause was trigger-module, continue
// else dont change trigger configuration
$sformatf("csrr x%0d, 0x%0x", cfg.scratch_reg, DCSR),
$sformatf("slli x%0d, x%0d, 0x17", cfg.scratch_reg, cfg.scratch_reg),
$sformatf("srli x%0d, x%0d, 0x1d", cfg.scratch_reg, cfg.scratch_reg),
$sformatf("li x%0d, 0x2", cfg.gpr[0]),
$sformatf("bne x%0d, x%0d, 3f", cfg.scratch_reg, cfg.gpr[0]),

$sformatf("csrr x%0d, 0x%0x", cfg.scratch_reg, DSCRATCH0),
// if the counter is greater than 0, decrement and continue further trigger iterations
$sformatf("bgtz x%0d, 2f", cfg.scratch_reg),
$sformatf("csrc 0x%0x, 0x4", TDATA1),
//Set TDATA2 = 0xffff_ffff as a hint for sw to skip any
//subsequent debug rom entry from re-enabling trigger execute
$sformatf("li x%0d, 0xffffffff", cfg.scratch_reg),
$sformatf("csrw 0x%0x, x%0d", TDATA2, cfg.scratch_reg),
$sformatf("beqz x0, 3f"),
// Set tdata1.execute, set tdata2 and the num_iterations counter, if
// TDATA2 != 0xffff_ffff
$sformatf("1: csrr x%0d, 0x%0x", cfg.scratch_reg, TDATA2),
$sformatf("li x%0d, 0xffffffff", cfg.gpr[0]),
$sformatf("beq x%0d, x%0d, 3f", cfg.scratch_reg, cfg.gpr[0]),
$sformatf("csrs 0x%0x, 0x4", TDATA1),
$sformatf("csrr x%0d, 0x%0x", cfg.scratch_reg, DPC),
$sformatf("addi x%0d, x%0d, %0d", cfg.scratch_reg, cfg.scratch_reg, cfg_corev.trigger_addr_offset),
$sformatf("csrw 0x%0x, x%0d", TDATA2, cfg.scratch_reg),
$sformatf("li x%0d, %0d", cfg.scratch_reg, cfg_corev.trigger_iterations),
$sformatf("csrw 0x%0x, x%0d", DSCRATCH0, cfg.scratch_reg),
$sformatf("beqz x0, 3f"),
// Decrement dscratch counter and set tdata2
$sformatf("2: csrr x%0d, 0x%0x", cfg.scratch_reg, DSCRATCH0),
$sformatf("addi x%0d, x%0d, -1", cfg.scratch_reg, cfg.scratch_reg),
$sformatf("csrw 0x%0x, x%0d", DSCRATCH0, cfg.scratch_reg),
$sformatf("csrr x%0d, 0x%0x", cfg.scratch_reg, DPC),
$sformatf("addi x%0d, x%0d, %0d", cfg.scratch_reg, cfg.scratch_reg, cfg_corev.trigger_addr_offset),
$sformatf("csrw 0x%0x, x%0d", TDATA2, cfg.scratch_reg),
// Restore scratch_reg value from dscratch1
$sformatf("3: csrr x%0d, 0x%0x", cfg.scratch_reg, DSCRATCH1)};
end
else begin
str = {$sformatf("csrw 0x%0x, x%0d", DSCRATCH1, cfg.scratch_reg),
// Only un-set tdata1.execute if it is 1 and the iterations counter
// is at 0 (has finished expected iterations)
$sformatf("csrr x%0d, 0x%0x", cfg.scratch_reg, TDATA1),
$sformatf("andi x%0d, x%0d, 4", cfg.scratch_reg, cfg.scratch_reg),
// If tdata1.execute is 0, set to 1 else clear
$sformatf("beqz x%0d, 1f", cfg.scratch_reg),
// Check DCSR.cause, if cause was trigger-module, continue
// else dont change trigger configuration
$sformatf("csrr x%0d, 0x%0x", cfg.scratch_reg, DCSR),
$sformatf("slli x%0d, x%0d, 0x17", cfg.scratch_reg, cfg.scratch_reg),
$sformatf("srli x%0d, x%0d, 0x1d", cfg.scratch_reg, cfg.scratch_reg),
$sformatf("li x%0d, 0x2", cfg.gpr[0]),
$sformatf("bne x%0d, x%0d, 3f", cfg.scratch_reg, cfg.gpr[0]),

$sformatf("csrc 0x%0x, 0x4", TDATA1),
//Set TDATA2 = 0xffff_ffff as a hint for sw to skip any
//subsequent debug rom entry from re-enabling trigger execute
$sformatf("li x%0d, 0xffffffff", cfg.scratch_reg),
$sformatf("csrw 0x%0x, x%0d", TDATA2, cfg.scratch_reg),
$sformatf("beqz x0, 3f"),
// Set tdata1.execute, set tdata2, if
// TDATA2 != 0xffff_ffff
$sformatf("1: csrr x%0d, 0x%0x", cfg.scratch_reg, TDATA2),
$sformatf("li x%0d, 0xffffffff", cfg.gpr[0]),
$sformatf("beq x%0d, x%0d, 3f", cfg.scratch_reg, cfg.gpr[0]),
$sformatf("csrs 0x%0x, 0x4", TDATA1),
$sformatf("csrr x%0d, 0x%0x", cfg.scratch_reg, DPC),
$sformatf("addi x%0d, x%0d, %0d", cfg.scratch_reg, cfg.scratch_reg, cfg_corev.trigger_addr_offset),
$sformatf("csrw 0x%0x, x%0d", TDATA2, cfg.scratch_reg),
$sformatf("beqz x0, 3f"),
// Restore scratch_reg value from dscratch1
$sformatf("3: csrr x%0d, 0x%0x", cfg.scratch_reg, DSCRATCH1)};
end
debug_main = {debug_main, str};

endfunction

// Function: gen_single_step_logic_if_trigger_ebreak
// Description: The logic in this function is to setup single stepping,
// after 1 debug trigger or debug ebreak is triggered.
// This covers cases with trigger/ebreak and single step
// Debug entry cause Trigger/Ebreak -> Single Step Enable
virtual function void gen_single_step_logic_if_trigger_ebreak();
str = {$sformatf("csrw 0x%0x, x%0d", DSCRATCH1, cfg.scratch_reg),
// Only un-set dcsr.step if it is 1 and the iterations counter
// is at 0 (has finished iterating)
$sformatf("csrr x%0d, 0x%0x", cfg.scratch_reg, DCSR),
$sformatf("andi x%0d, x%0d, 4", cfg.scratch_reg, cfg.scratch_reg),
// If dcsr.step is 0, set to 1 and set the counter, if
// trigger was the debug entry cause
$sformatf("beqz x%0d, 1f", cfg.scratch_reg),
// Check DCSR.cause, if cause was step, continue
// else dont change step configuration
$sformatf("csrr x%0d, 0x%0x", cfg.scratch_reg, DCSR),
$sformatf("slli x%0d, x%0d, 0x17", cfg.scratch_reg, cfg.scratch_reg),
$sformatf("srli x%0d, x%0d, 0x1d", cfg.scratch_reg, cfg.scratch_reg),
$sformatf("li x%0d, 0x4", cfg.gpr[0]),
$sformatf("bne x%0d, x%0d, 3f", cfg.scratch_reg, cfg.gpr[0]),

$sformatf("csrr x%0d, 0x%0x", cfg.scratch_reg, DSCRATCH0),
// if the counter is greater than 0, decrement and continue single stepping
$sformatf("bgtz x%0d, 2f", cfg.scratch_reg),
$sformatf("csrc 0x%0x, 0x4", DCSR),
//Set DSCRATCH0 = 0xffff_ffff as a hint for sw to skip any
//subsequent debug rom entry from re-enabling step
$sformatf("li x%0d, 0xffffffff", cfg.scratch_reg),
$sformatf("csrw 0x%0x, x%0d", DSCRATCH0, cfg.scratch_reg),
$sformatf("beqz x0, 3f"),
// Check DCSR.cause, if cause was trigger-module breakpoint or ebreak, set dcsr.step
// else dont change configuration
$sformatf("1: csrr x%0d, 0x%0x", cfg.scratch_reg, DCSR),
$sformatf("slli x%0d, x%0d, 0x17", cfg.scratch_reg, cfg.scratch_reg),
$sformatf("srli x%0d, x%0d, 0x1d", cfg.scratch_reg, cfg.scratch_reg),
$sformatf("li x%0d, 0x1", cfg.gpr[0]),
$sformatf("beq x%0d, x%0d, 11f", cfg.scratch_reg, cfg.gpr[0]),
$sformatf("li x%0d, 0x2", cfg.gpr[0]),
$sformatf("beq x%0d, x%0d, 11f", cfg.scratch_reg, cfg.gpr[0]),
$sformatf("beqz x0, 3f"),
// Set dcsr.step and the num_iterations counter, if
// DSCRATCH0 != 0xffff_ffff
$sformatf("11: csrr x%0d, 0x%0x", cfg.scratch_reg, DSCRATCH0),
$sformatf("li x%0d, 0xffffffff", cfg.gpr[0]),
$sformatf("beq x%0d, x%0d, 3f", cfg.scratch_reg, cfg.gpr[0]),
$sformatf("csrs 0x%0x, 0x4", DCSR),
$sformatf("li x%0d, %0d", cfg.scratch_reg, cfg.single_step_iterations),
$sformatf("csrw 0x%0x, x%0d", DSCRATCH0, cfg.scratch_reg),
$sformatf("beqz x0, 3f"),
// Decrement dscratch counter
$sformatf("2: csrr x%0d, 0x%0x", cfg.scratch_reg, DSCRATCH0),
$sformatf("addi x%0d, x%0d, -1", cfg.scratch_reg, cfg.scratch_reg),
$sformatf("csrw 0x%0x, x%0d", DSCRATCH0, cfg.scratch_reg),
// Restore scratch_reg value from dscratch1
$sformatf("3: csrr x%0d, 0x%0x", cfg.scratch_reg, DSCRATCH1)
};
debug_main = {debug_main, str};
// write dpc to testbench
gen_signature_handshake(.instr(debug_main), .signature_type(WRITE_CSR), .csr(DPC));
// write out the counter to the testbench
gen_signature_handshake(.instr(debug_main), .signature_type(WRITE_CSR), .csr(DSCRATCH0));
endfunction

endclass : cv32e40p_debug_rom_gen

14 changes: 13 additions & 1 deletion cv32e40p/env/corev-dv/cv32e40p_illegal_instr.sv
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,18 @@ class cv32e40p_illegal_instr extends riscv_illegal_instr;

function new(string name="");
super.new(name);
endfunction
endfunction

function void cv32e40p_init(riscv_instr_gen_config cfg);
this.cfg = cfg;
if (riscv_instr_pkg::RV32ZFINX inside {riscv_instr_pkg::supported_isa}) begin
legal_opcode = {legal_opcode, 7'b1000011, 7'b1000111, 7'b1001011,
7'b1001111, 7'b1010011};
end
if (riscv_instr_pkg::RV32X inside {riscv_instr_pkg::supported_isa}) begin
legal_opcode = {legal_opcode, 7'b0001011, 7'b0101011, 7'b1011011,
7'b1111011};
end
endfunction

endclass : cv32e40p_illegal_instr
35 changes: 35 additions & 0 deletions cv32e40p/env/corev-dv/cv32e40p_instr_gen_config.sv
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,23 @@ class cv32e40p_instr_gen_config extends riscv_instr_gen_config;
rand riscv_reg_t str_rs1;
rand riscv_reg_t str_rs3;

// Enable debug trigger breakpoint setup in debug rom
bit setup_debug_trigger_on_addr_match = 0;

// Number of trigger iterations in a given test
rand int trigger_iterations;

// Random offset to program next trigger address
rand int trigger_addr_offset;

//random field to enable setting of single stepping only after
//a trigger breakpoint causes debug entry
rand bit debug_trigger_before_single_step;

//random field to enable setting of single stepping only after
//an ebreak causes debug entry
rand bit debug_ebreak_before_single_step;

constraint ss_dbg_high_iteration_cnt_c {
ss_dbg_high_iteration_cnt dist {0 := 60, 1 := 40};
}
Expand Down Expand Up @@ -164,6 +181,12 @@ class cv32e40p_instr_gen_config extends riscv_instr_gen_config;
}
}

constraint trigger_c {
trigger_iterations inside {[1:10]};
trigger_addr_offset inside {[4:100]};
trigger_addr_offset % 4 == 0;
}

`uvm_object_utils_begin(cv32e40p_instr_gen_config)
`uvm_field_enum(mtvec_mode_t, mtvec_mode, UVM_DEFAULT)
`uvm_field_int(knob_zero_fast_intr_handlers, UVM_DEFAULT)
Expand All @@ -181,6 +204,11 @@ class cv32e40p_instr_gen_config extends riscv_instr_gen_config;
`uvm_field_int(xpulp_instr_in_debug_rom, UVM_DEFAULT)
`uvm_field_enum(riscv_reg_t, str_rs1, UVM_DEFAULT)
`uvm_field_enum(riscv_reg_t, str_rs3, UVM_DEFAULT)
`uvm_field_int(setup_debug_trigger_on_addr_match, UVM_DEFAULT)
`uvm_field_int(trigger_iterations, UVM_DEFAULT)
`uvm_field_int(trigger_addr_offset, UVM_DEFAULT)
`uvm_field_int(debug_trigger_before_single_step, UVM_DEFAULT)
`uvm_field_int(debug_ebreak_before_single_step, UVM_DEFAULT)
`uvm_object_utils_end

function new(string name="");
Expand All @@ -190,6 +218,13 @@ class cv32e40p_instr_gen_config extends riscv_instr_gen_config;
get_bool_arg_value("+insert_rand_directed_instr_stream=", insert_rand_directed_instr_stream);
get_int_arg_value("+test_rand_directed_instr_stream_num=", test_rand_directed_instr_stream_num);
get_bool_arg_value("+is_hwloop_test=", is_hwloop_test);
get_bool_arg_value("+setup_debug_trigger_on_addr_match=", setup_debug_trigger_on_addr_match);
get_bool_arg_value("+debug_trigger_before_single_step=", debug_trigger_before_single_step);
get_bool_arg_value("+debug_ebreak_before_single_step=", debug_ebreak_before_single_step);

if ($test$plusargs("debug_trigger_before_single_step")) debug_trigger_before_single_step.rand_mode(0);
if ($test$plusargs("debug_ebreak_before_single_step")) debug_ebreak_before_single_step.rand_mode(0);

endfunction : new

function void post_randomize();
Expand Down
Loading

0 comments on commit f70c820

Please sign in to comment.