[dv] Ibex uarch functional coverage

This adds a framework for gathering functional coverage for Ibex
microarchitecture along with a selection of initial coverpoints.
This commit is contained in:
Greg Chadwick 2021-01-14 17:08:48 +00:00
parent d717e2385e
commit 794d865f56
11 changed files with 251 additions and 0 deletions

17
dv/fcov/dv_fcov.core Normal file
View file

@ -0,0 +1,17 @@
CAPI=2:
# Copyright lowRISC contributors.
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
# SPDX-License-Identifier: Apache-2.0
name: "lowrisc:dv:dv_fcov"
description: "DV FCOV utilities"
filesets:
files_fcov:
files:
- dv_fcov_macros.svh: {is_include_file: true}
file_type: systemVerilogSource
targets:
default:
filesets:
- files_fcov

View file

@ -0,0 +1,48 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
// Coverage support is not available in Verilator but it's useful to include extra fcov signals for
// linting purposes. They need to be marked as unused to avoid warnings.
// Include FCOV RTL by default. Exclude it for synthesis and where explicitly requested (by defining
// EXCLUDE_FCOV).
`ifdef SYNTHESIS
`define EXCLUDE_FCOV
`elsif YOSYS
`define EXCLUDE_FCOV
`endif
`ifdef VERILATOR
`define FCOV_MARK_UNUSED(__var_type, __var_name) \
__var_type unused_fcov_``__var_name; \
assign unused_fcov_``__var_name = fcov_``__var_name;
`else
`define FCOV_MARK_UNUSED(__var_type, __var_name)
`endif
`ifndef FCOV_SIGNAL
`define FCOV_SIGNAL(__var_type, __var_name, __var_definition) \
`ifndef EXCLUDE_FCOV \
__var_type fcov_``__var_name; \
\
assign fcov_``__var_name = __var_definition; \
\
`FCOV_MARK_UNUSED(__var_type, __var_name) \
`endif
`endif
`ifndef FCOV_SIGNAL_GEN_IF
`define FCOV_SIGNAL_GEN_IF(__var_type, __var_name, __var_definition, __generate_test, __default_val = '0) \
`ifndef EXCLUDE_FCOV \
__var_type fcov_``__var_name; \
\
if (__generate_test) begin : g_fcov_``__var_name \
assign fcov_``__var_name = __var_definition; \
end else begin : g_no_fcov_``__var_name \
assign fcov_``__var_name = __default_val; \
end \
\
`FCOV_MARK_UNUSED(__var_type, __var_name) \
`endif
`endif

View file

@ -0,0 +1,9 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
module core_ibex_fcov_bind;
bind ibex_core core_ibex_fcov_if u_fcov_bind (
.*
);
endmodule

View file

@ -0,0 +1,141 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
interface core_ibex_fcov_if import ibex_pkg::*; (
input clk_i,
input rst_ni,
input priv_lvl_e priv_mode_id,
input priv_lvl_e priv_mode_if,
input priv_lvl_e priv_mode_lsu
);
`include "dv_macros.svh"
import uvm_pkg::*;
typedef enum {
InsnCategoryALU,
InsnCategoryMult,
InsnCategoryDiv,
InsnCategoryBranch,
InsnCategoryJump,
InsnCategoryLoad,
InsnCategoryStore,
InsnCategoryOther,
InsnCategoryNone
} insn_category_e;
typedef enum {
IdStallTypeNone,
IdStallTypeInsn,
IdStallTypeLdHz,
IdStallTypeMem
} id_stall_type_e;
insn_category_e id_instr_category;
// Set `id_instr_category` to the appropriate category for the uncompressed instruction in the
// ID/EX stage. Compressed instructions are not handled (`id_stage_i.instr_rdata_i` is always
// uncompressed). When the `id_stage.instr_valid_i` isn't set `InsnCategoryNone` is the given
// instruction category.
// TODO: Illegal instructions
always_comb begin
case (id_stage_i.instr_rdata_i[6:0])
ibex_pkg::OPCODE_LUI: id_instr_category = InsnCategoryALU;
ibex_pkg::OPCODE_AUIPC: id_instr_category = InsnCategoryALU;
ibex_pkg::OPCODE_JAL: id_instr_category = InsnCategoryJump;
ibex_pkg::OPCODE_JALR: id_instr_category = InsnCategoryJump;
ibex_pkg::OPCODE_BRANCH: id_instr_category = InsnCategoryBranch;
ibex_pkg::OPCODE_LOAD: id_instr_category = InsnCategoryLoad;
ibex_pkg::OPCODE_STORE: id_instr_category = InsnCategoryStore;
ibex_pkg::OPCODE_OP_IMM: id_instr_category = InsnCategoryALU;
ibex_pkg::OPCODE_OP: begin
if(id_stage_i.instr_rdata_i[31:25] == 7'b0000000) begin
id_instr_category = InsnCategoryALU; // RV32I ALU reg-reg ops
end else if (id_stage_i.instr_rdata_i[31:25] == 7'b0000001) begin
if (id_stage_i.instr_rdata_i[14]) begin
id_instr_category = InsnCategoryMult; //MUL*
end else begin
id_instr_category = InsnCategoryDiv; // DIV*
end
end
end
default: id_instr_category = InsnCategoryOther;
endcase
if (!id_stage_i.instr_valid_i) begin
id_instr_category = InsnCategoryNone;
end
end
id_stall_type_e id_stall_type;
// Set `id_stall_type` to the appropriate type based on signals in the ID/EX stage
always_comb begin
id_stall_type = IdStallTypeNone;
if (id_stage_i.instr_valid_i) begin
if (id_stage_i.stall_mem) begin
id_stall_type = IdStallTypeMem;
end
if (id_stage_i.stall_ld_hz) begin
id_stall_type = IdStallTypeLdHz;
end
if (id_stage_i.stall_multdiv || id_stage_i.stall_branch ||
id_stage_i.stall_jump) begin
id_stall_type = IdStallTypeInsn;
end
end
end
`DV_FCOV_SVA(instruction_unstalled, id_stall_type != IdStallTypeNone ##1
id_stall_type == IdStallTypeNone)
covergroup uarch_cg @(posedge clk_i);
type_option.strobe = 1;
`DV_FCOV_EXPR_SEEN(cp_insn_unstalled, instruction_unstalled.triggered)
cp_insn_category_id: coverpoint id_instr_category;
cp_stall_type_id: coverpoint id_stall_type;
cp_wb_reg_hz: coverpoint id_stage_i.fcov_rf_rd_wb_hz;
cp_wb_load_hz: coverpoint id_stage_i.fcov_rf_rd_wb_hz &&
wb_stage_i.outstanding_load_wb_o;
cp_ls_error_exception: coverpoint load_store_unit_i.fcov_ls_error_exception;
cp_ls_pmp_exception: coverpoint load_store_unit_i.fcov_ls_pmp_exception;
cp_branch_taken: coverpoint id_stage_i.fcov_branch_taken;
cp_branch_not_taken: coverpoint id_stage_i.fcov_branch_not_taken;
cp_priv_mode_id: coverpoint priv_mode_id;
cp_priv_mode_if: coverpoint priv_mode_if;
cp_prov_mode_lsu: coverpoint priv_mode_lsu;
`DV_FCOV_EXPR_SEEN(cp_interrupt_taken, id_stage_i.controller_i.fcov_interrupt_taken)
`DV_FCOV_EXPR_SEEN(cp_debug_entry_if, id_stage_i.controller_i.fcov_debug_entry_if)
`DV_FCOV_EXPR_SEEN(cp_debug_entry_id, id_stage_i.controller_i.fcov_debug_entry_id)
`DV_FCOV_EXPR_SEEN(cp_pipe_flush, id_stage_i.controller_i.fcov_pipe_flush)
wb_reg_hz_instr_cross: cross cp_insn_category_id, cp_wb_reg_hz;
stall_cross: cross cp_insn_category_id, cp_stall_type_id;
pipe_cross: cross cp_insn_category_id, if_stage_i.if_instr_valid,
wb_stage_i.fcov_wb_valid;
interrupt_taken_instr_cross: cross cp_insn_unstalled, cp_interrupt_taken, cp_insn_category_id;
debug_entry_if_instr_cross: cross cp_debug_entry_if, cp_insn_category_id;
pipe_flush_instr_cross: cross cp_pipe_flush, cp_insn_category_id;
endgroup
bit en_uarch_cov;
initial begin
void'($value$plusargs("enable_uarch_cov=%d", en_uarch_cov));
end
`DV_INSTANTIATE_CG(uarch_cg, en_uarch_cov)
endinterface

View file

@ -65,6 +65,7 @@ ${PRJ_DIR}/vendor/google_riscv-dv/src/riscv_signature_pkg.sv
+incdir+${PRJ_DIR}/dv/uvm/core_ibex/tests
+incdir+${PRJ_DIR}/dv/uvm/core_ibex/common/ibex_mem_intf_agent
+incdir+${PRJ_DIR}/dv/uvm/core_ibex/common/irq_agent
+incdir+${PRJ_DIR}/dv/fcov
+incdir+${PRJ_DIR}/vendor/lowrisc_ip/dv/sv/mem_model
+incdir+${PRJ_DIR}/vendor/lowrisc_ip/dv/sv/dv_utils
+incdir+${PRJ_DIR}/vendor/lowrisc_ip/dv/sv/str_utils
@ -85,4 +86,6 @@ ${PRJ_DIR}/dv/uvm/core_ibex/env/core_ibex_rvfi_if.sv
${PRJ_DIR}/dv/uvm/core_ibex/env/core_ibex_csr_if.sv
${PRJ_DIR}/dv/uvm/core_ibex/env/core_ibex_env_pkg.sv
${PRJ_DIR}/dv/uvm/core_ibex/tests/core_ibex_test_pkg.sv
${PRJ_DIR}/dv/uvm/core_ibex/fcov/core_ibex_fcov_if.sv
${PRJ_DIR}/dv/uvm/core_ibex/fcov/core_ibex_fcov_bind.sv
${PRJ_DIR}/dv/uvm/core_ibex/tb/core_ibex_tb_top.sv

View file

@ -55,6 +55,7 @@
-cm_log /dev/null
-assert nopostproc
-cm_name test_<test_name>_<iteration>
+enable_uarch_cov=1
wave_opts: >
-ucli -do <cwd>/vcs.tcl

View file

@ -13,6 +13,7 @@ filesets:
- lowrisc:prim:lfsr
- lowrisc:ibex:ibex_pkg
- lowrisc:ibex:ibex_icache
- lowrisc:dv:dv_fcov
files:
- rtl/ibex_alu.sv
- rtl/ibex_branch_predict.sv

View file

@ -8,6 +8,7 @@
*/
`include "prim_assert.sv"
`include "dv_fcov_macros.svh"
module ibex_controller #(
parameter bit WritebackStage = 0,
@ -829,6 +830,14 @@ module ibex_controller #(
end
end
//////////
// FCOV //
//////////
`FCOV_SIGNAL(logic, interrupt_taken, (ctrl_fsm_cs != IRQ_TAKEN) & (ctrl_fsm_ns == IRQ_TAKEN));
`FCOV_SIGNAL(logic, debug_entry_if, (ctrl_fsm_cs != DBG_TAKEN_IF) & (ctrl_fsm_ns == DBG_TAKEN_IF));
`FCOV_SIGNAL(logic, debug_entry_id, (ctrl_fsm_cs != DBG_TAKEN_ID) & (ctrl_fsm_ns == DBG_TAKEN_ID));
`FCOV_SIGNAL(logic, pipe_flush, (ctrl_fsm_cs != FLUSH) & (ctrl_fsm_ns == FLUSH));
////////////////
// Assertions //
////////////////

View file

@ -15,6 +15,7 @@
*/
`include "prim_assert.sv"
`include "dv_fcov_macros.svh"
module ibex_id_stage #(
parameter bit RV32E = 0,
@ -990,6 +991,17 @@ module ibex_id_stage #(
assign perf_mul_wait_o = stall_multdiv & mult_en_dec;
assign perf_div_wait_o = stall_multdiv & div_en_dec;
//////////
// FCOV //
//////////
`FCOV_SIGNAL_GEN_IF(logic, rf_rd_wb_hz,
(gen_stall_mem.rf_rd_a_hz | gen_stall_mem.rf_rd_b_hz) & instr_valid_i, WritebackStage)
`FCOV_SIGNAL(logic, branch_taken,
instr_executing & (id_fsm_q == FIRST_CYCLE) & branch_decision_i);
`FCOV_SIGNAL(logic, branch_not_taken,
instr_executing & (id_fsm_q == FIRST_CYCLE) & ~branch_decision_i);
////////////////
// Assertions //
////////////////

View file

@ -12,6 +12,7 @@
*/
`include "prim_assert.sv"
`include "dv_fcov_macros.svh"
module ibex_load_store_unit
(
@ -494,6 +495,12 @@ module ibex_load_store_unit
assign busy_o = (ls_fsm_cs != IDLE);
//////////
// FCOV //
//////////
`FCOV_SIGNAL(logic, ls_error_exception, (load_err_o | store_err_o) & ~pmp_err_q);
`FCOV_SIGNAL(logic, ls_pmp_exception, (load_err_o | store_err_o) & pmp_err_q);
////////////////
// Assertions //
////////////////

View file

@ -12,6 +12,7 @@
*/
`include "prim_assert.sv"
`include "dv_fcov_macros.svh"
module ibex_wb_stage #(
parameter bit WritebackStage = 1'b0
@ -172,5 +173,7 @@ module ibex_wb_stage #(
assign rf_wdata_wb_o = rf_wdata_wb_mux_we[0] ? rf_wdata_wb_mux[0] : rf_wdata_wb_mux[1];
assign rf_we_wb_o = |rf_wdata_wb_mux_we;
`FCOV_SIGNAL_GEN_IF(logic, wb_valid, g_writeback_stage.wb_valid_q, WritebackStage)
`ASSERT(RFWriteFromOneSourceOnly, $onehot0(rf_wdata_wb_mux_we))
endmodule