diff --git a/dv/fcov/dv_fcov.core b/dv/fcov/dv_fcov.core new file mode 100644 index 00000000..32688bcb --- /dev/null +++ b/dv/fcov/dv_fcov.core @@ -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 diff --git a/dv/fcov/dv_fcov_macros.svh b/dv/fcov/dv_fcov_macros.svh new file mode 100644 index 00000000..2f43cb13 --- /dev/null +++ b/dv/fcov/dv_fcov_macros.svh @@ -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 diff --git a/dv/uvm/core_ibex/fcov/core_ibex_fcov_bind.sv b/dv/uvm/core_ibex/fcov/core_ibex_fcov_bind.sv new file mode 100644 index 00000000..9ea088df --- /dev/null +++ b/dv/uvm/core_ibex/fcov/core_ibex_fcov_bind.sv @@ -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 diff --git a/dv/uvm/core_ibex/fcov/core_ibex_fcov_if.sv b/dv/uvm/core_ibex/fcov/core_ibex_fcov_if.sv new file mode 100644 index 00000000..7659f77e --- /dev/null +++ b/dv/uvm/core_ibex/fcov/core_ibex_fcov_if.sv @@ -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 diff --git a/dv/uvm/core_ibex/ibex_dv.f b/dv/uvm/core_ibex/ibex_dv.f index 08bab062..a9848731 100644 --- a/dv/uvm/core_ibex/ibex_dv.f +++ b/dv/uvm/core_ibex/ibex_dv.f @@ -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 diff --git a/dv/uvm/core_ibex/yaml/rtl_simulation.yaml b/dv/uvm/core_ibex/yaml/rtl_simulation.yaml index fbed3349..7de42533 100644 --- a/dv/uvm/core_ibex/yaml/rtl_simulation.yaml +++ b/dv/uvm/core_ibex/yaml/rtl_simulation.yaml @@ -55,6 +55,7 @@ -cm_log /dev/null -assert nopostproc -cm_name test__ + +enable_uarch_cov=1 wave_opts: > -ucli -do /vcs.tcl diff --git a/ibex_core.core b/ibex_core.core index 05b33ccd..f153e12d 100644 --- a/ibex_core.core +++ b/ibex_core.core @@ -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 diff --git a/rtl/ibex_controller.sv b/rtl/ibex_controller.sv index db98a0ae..a964c435 100644 --- a/rtl/ibex_controller.sv +++ b/rtl/ibex_controller.sv @@ -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 // //////////////// diff --git a/rtl/ibex_id_stage.sv b/rtl/ibex_id_stage.sv index 7c91eae1..aa1a9dd3 100644 --- a/rtl/ibex_id_stage.sv +++ b/rtl/ibex_id_stage.sv @@ -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 // //////////////// diff --git a/rtl/ibex_load_store_unit.sv b/rtl/ibex_load_store_unit.sv index 4d89b257..8fb09a95 100644 --- a/rtl/ibex_load_store_unit.sv +++ b/rtl/ibex_load_store_unit.sv @@ -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 // //////////////// diff --git a/rtl/ibex_wb_stage.sv b/rtl/ibex_wb_stage.sv index 7299ad11..7075c01b 100644 --- a/rtl/ibex_wb_stage.sv +++ b/rtl/ibex_wb_stage.sv @@ -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