| // Copyright lowRISC contributors. |
| // Licensed under the Apache License, Version 2.0, see LICENSE for details. |
| // SPDX-License-Identifier: Apache-2.0 |
| |
| /** |
| * Covergoups that are dependent on run-time parameters that may be available |
| * only in build_phase can be defined here |
| * Covergroups may also be wrapped inside helper classes if needed. |
| */ |
| |
| class otbn_env_cov extends cip_base_env_cov #(.CFG_T(otbn_env_cfg)); |
| `uvm_component_utils(otbn_env_cov) |
| |
| // A field for each known mnemonic, cast to a mnem_str_t. We have to do this because VCS (at |
| // least) complains if you put an uncast string literal in a position where it expects an integral |
| // value. |
| `define DEF_MNEM(MNEM_NAME, MNEMONIC) \ |
| mnem_str_t MNEM_NAME = mnem_str_t'(MNEMONIC) |
| `DEF_MNEM(mnem_add, "add"); |
| `DEF_MNEM(mnem_addi, "addi"); |
| `DEF_MNEM(mnem_lui, "lui"); |
| `DEF_MNEM(mnem_sub, "sub"); |
| `DEF_MNEM(mnem_sll, "sll"); |
| `DEF_MNEM(mnem_slli, "slli"); |
| `DEF_MNEM(mnem_srl, "srl"); |
| `DEF_MNEM(mnem_srli, "srli"); |
| `DEF_MNEM(mnem_sra, "sra"); |
| `DEF_MNEM(mnem_srai, "srai"); |
| `DEF_MNEM(mnem_and, "and"); |
| `DEF_MNEM(mnem_andi, "andi"); |
| `DEF_MNEM(mnem_or, "or"); |
| `DEF_MNEM(mnem_ori, "ori"); |
| `DEF_MNEM(mnem_xor, "xor"); |
| `DEF_MNEM(mnem_xori, "xori"); |
| `DEF_MNEM(mnem_lw, "lw"); |
| `DEF_MNEM(mnem_sw, "sw"); |
| `DEF_MNEM(mnem_beq, "beq"); |
| `DEF_MNEM(mnem_bne, "bne"); |
| `DEF_MNEM(mnem_jal, "jal"); |
| `DEF_MNEM(mnem_jalr, "jalr"); |
| `DEF_MNEM(mnem_csrrs, "csrrs"); |
| `DEF_MNEM(mnem_csrrw, "csrrw"); |
| `DEF_MNEM(mnem_ecall, "ecall"); |
| `DEF_MNEM(mnem_loop, "loop"); |
| `DEF_MNEM(mnem_loopi, "loopi"); |
| `DEF_MNEM(mnem_bn_add, "bn.add"); |
| `DEF_MNEM(mnem_bn_addc, "bn.addc"); |
| `DEF_MNEM(mnem_bn_addi, "bn.addi"); |
| `DEF_MNEM(mnem_bn_addm, "bn.addm"); |
| `DEF_MNEM(mnem_bn_mulqacc, "bn.mulqacc"); |
| `DEF_MNEM(mnem_bn_mulqacc_wo, "bn.mulqacc.wo"); |
| `DEF_MNEM(mnem_bn_mulqacc_so, "bn.mulqacc.so"); |
| `DEF_MNEM(mnem_bn_sub, "bn.sub"); |
| `DEF_MNEM(mnem_bn_subb, "bn.subb"); |
| `DEF_MNEM(mnem_bn_subi, "bn.subi"); |
| `DEF_MNEM(mnem_bn_subm, "bn.subm"); |
| `DEF_MNEM(mnem_bn_and, "bn.and"); |
| `DEF_MNEM(mnem_bn_or, "bn.or"); |
| `DEF_MNEM(mnem_bn_not, "bn.not"); |
| `DEF_MNEM(mnem_bn_xor, "bn.xor"); |
| `DEF_MNEM(mnem_bn_rshi, "bn.rshi"); |
| `DEF_MNEM(mnem_bn_sel, "bn.sel"); |
| `DEF_MNEM(mnem_bn_cmp, "bn.cmp"); |
| `DEF_MNEM(mnem_bn_cmpb, "bn.cmpb"); |
| `DEF_MNEM(mnem_bn_lid, "bn.lid"); |
| `DEF_MNEM(mnem_bn_sid, "bn.sid"); |
| `DEF_MNEM(mnem_bn_mov, "bn.mov"); |
| `DEF_MNEM(mnem_bn_movr, "bn.movr"); |
| `DEF_MNEM(mnem_bn_wsrr, "bn.wsrr"); |
| `DEF_MNEM(mnem_bn_wsrw, "bn.wsrw"); |
| // A fake mnemonic, used for bits that don't decode to a real instruction |
| `DEF_MNEM(mnem_dummy, "dummy-insn"); |
| // A fake mnemonic, used for invalid IMEM data (after a failed integrity check) |
| `DEF_MNEM(mnem_question_mark, "??"); |
| `undef DEF_MNEM |
| |
| // A macro used for coverpoints for mnemonics. This expands to entries like |
| // |
| // bins mnem_add = {mnem_add}; |
| `define DEF_MNEM_BIN(NAME) bins NAME = {NAME} |
| |
| // Generate a bin for each mnemonic except ECALL |
| `define DEF_MNEM_BINS_EXCEPT_ECALL \ |
| `DEF_MNEM_BIN(mnem_add); `DEF_MNEM_BIN(mnem_addi); \ |
| `DEF_MNEM_BIN(mnem_lui); `DEF_MNEM_BIN(mnem_sub); \ |
| `DEF_MNEM_BIN(mnem_sll); `DEF_MNEM_BIN(mnem_slli); \ |
| `DEF_MNEM_BIN(mnem_srl); `DEF_MNEM_BIN(mnem_srli); \ |
| `DEF_MNEM_BIN(mnem_sra); `DEF_MNEM_BIN(mnem_srai); \ |
| `DEF_MNEM_BIN(mnem_and); `DEF_MNEM_BIN(mnem_andi); \ |
| `DEF_MNEM_BIN(mnem_or); `DEF_MNEM_BIN(mnem_ori); \ |
| `DEF_MNEM_BIN(mnem_xor); `DEF_MNEM_BIN(mnem_xori); \ |
| `DEF_MNEM_BIN(mnem_lw); `DEF_MNEM_BIN(mnem_sw); \ |
| `DEF_MNEM_BIN(mnem_beq); `DEF_MNEM_BIN(mnem_bne); \ |
| `DEF_MNEM_BIN(mnem_jal); `DEF_MNEM_BIN(mnem_jalr); \ |
| `DEF_MNEM_BIN(mnem_csrrs); `DEF_MNEM_BIN(mnem_csrrw); \ |
| `DEF_MNEM_BIN(mnem_loop); `DEF_MNEM_BIN(mnem_loopi); \ |
| `DEF_MNEM_BIN(mnem_bn_add); `DEF_MNEM_BIN(mnem_bn_addc); \ |
| `DEF_MNEM_BIN(mnem_bn_addi); `DEF_MNEM_BIN(mnem_bn_addm); \ |
| `DEF_MNEM_BIN(mnem_bn_mulqacc); `DEF_MNEM_BIN(mnem_bn_mulqacc_wo); \ |
| `DEF_MNEM_BIN(mnem_bn_mulqacc_so); \ |
| `DEF_MNEM_BIN(mnem_bn_sub); `DEF_MNEM_BIN(mnem_bn_subb); \ |
| `DEF_MNEM_BIN(mnem_bn_subi); `DEF_MNEM_BIN(mnem_bn_subm); \ |
| `DEF_MNEM_BIN(mnem_bn_and); `DEF_MNEM_BIN(mnem_bn_or); \ |
| `DEF_MNEM_BIN(mnem_bn_not); `DEF_MNEM_BIN(mnem_bn_xor); \ |
| `DEF_MNEM_BIN(mnem_bn_rshi); \ |
| `DEF_MNEM_BIN(mnem_bn_sel); \ |
| `DEF_MNEM_BIN(mnem_bn_cmp); `DEF_MNEM_BIN(mnem_bn_cmpb); \ |
| `DEF_MNEM_BIN(mnem_bn_lid); `DEF_MNEM_BIN(mnem_bn_sid); \ |
| `DEF_MNEM_BIN(mnem_bn_mov); `DEF_MNEM_BIN(mnem_bn_movr); \ |
| `DEF_MNEM_BIN(mnem_bn_wsrr); `DEF_MNEM_BIN(mnem_bn_wsrw); |
| |
| // Equivalents of DEF_MNEM and DEF_MNEM_BINp, but for external CSRs. Again, we want to use the CSR |
| // names as bins in coverpoints and need sized literals. |
| `define DEF_CSR(CSR_NAME, STR) \ |
| csr_str_t CSR_NAME = csr_str_t'(STR) |
| `DEF_CSR(csr_load_checksum, "load_checksum"); |
| `DEF_CSR(csr_ctrl, "ctrl"); |
| `undef DEF_CSR |
| `define DEF_CSR_BIN(NAME) bins NAME = {NAME} |
| |
| // Cross one, two or three coverpoints with mnemonic_cp. |
| // |
| // This is intentended to be used inside covergroups that support multiple instructions. In each |
| // of these, we define a coverpoint called mnemonic_cp to track which instruction is being |
| // sampled. |
| `define DEF_MNEM_CROSS(BASENAME) \ |
| BASENAME``_cross: cross mnemonic_cp, BASENAME``_cp; |
| `define DEF_MNEM_CROSS2(BASE0, BASE1) \ |
| BASE0``_``BASE1``_cross: cross mnemonic_cp, BASE0``_cp, BASE1``_cp; |
| `define DEF_MNEM_CROSS3(BASE0, BASE1, BASE2) \ |
| BASE0``_``BASE1``_``BASE2``_cross: \ |
| cross mnemonic_cp, BASE0``_cp, BASE1``_cp, BASE2``_cp; |
| |
| // A macro to define bins for GPR types. The point is that there are 3 interesting types of GPR: |
| // x0, x1 and everything else. |
| `define GPR_BIN_TYPES \ |
| { bins gpr_x0 = {5'd0}; bins gpr_x1 = {5'd1}; bins gpr_other = {[5'd2:$]}; } |
| |
| // Declare a GPR coverpoint with the 3 types above |
| `define DEF_GPR_CP(NAME, BITS) \ |
| NAME: coverpoint insn_data[BITS] `GPR_BIN_TYPES |
| |
| // Macros for tracking "toggle coverage" of some bitfield. Use one of the DEF_*_TOGGLE_COV macros |
| // to define a coverpoint for each bit of the bitfield. |
| // |
| // The implementation uses macros to expand in powers of 2. The trick is that BIN_IDX will grow to |
| // give the base-2 representation of the index of the bit we're looking at. For example, the |
| // expansion of DEF_GPR_TOGGLE_COV(NAME, x) ends up with 32 calls to _DEF_TOGGLE_COV_1: |
| // |
| // _DEF_TOGGLE_COV_1(NAME, x, 5, 00000) |
| // ... |
| // _DEF_TOGGLE_COV_1(NAME, x, 5, 11111) |
| // |
| // This, in turn, expands to |
| // |
| // NAME_00000_cp: coverpoint x[5'b00000]; |
| // ... |
| // NAME_11111_cp: coverpoint x[5'b11111]; |
| // |
| // to track the 32 bits in x. |
| // |
| `define _DEF_TOGGLE_COV_1(BASE, BITS, IDXW, BIN_IDX) \ |
| BASE``_``BIN_IDX``_cp: coverpoint BITS[IDXW 'b BIN_IDX]; |
| `define _DEF_TOGGLE_COV_2(BASE, BITS, IDXW, BIN_IDX) \ |
| `_DEF_TOGGLE_COV_1(BASE, BITS, IDXW, BIN_IDX``0) \ |
| `_DEF_TOGGLE_COV_1(BASE, BITS, IDXW, BIN_IDX``1) |
| `define _DEF_TOGGLE_COV_4(BASE, BITS, IDXW, BIN_IDX) \ |
| `_DEF_TOGGLE_COV_2(BASE, BITS, IDXW, BIN_IDX``0) \ |
| `_DEF_TOGGLE_COV_2(BASE, BITS, IDXW, BIN_IDX``1) |
| `define _DEF_TOGGLE_COV_8(BASE, BITS, IDXW, BIN_IDX) \ |
| `_DEF_TOGGLE_COV_4(BASE, BITS, IDXW, BIN_IDX``0) \ |
| `_DEF_TOGGLE_COV_4(BASE, BITS, IDXW, BIN_IDX``1) |
| `define _DEF_TOGGLE_COV_16(BASE, BITS, IDXW, BIN_IDX) \ |
| `_DEF_TOGGLE_COV_8(BASE, BITS, IDXW, BIN_IDX``0) \ |
| `_DEF_TOGGLE_COV_8(BASE, BITS, IDXW, BIN_IDX``1) |
| `define _DEF_TOGGLE_COV_32(BASE, BITS, IDXW, BIN_IDX) \ |
| `_DEF_TOGGLE_COV_16(BASE, BITS, IDXW, BIN_IDX``0) \ |
| `_DEF_TOGGLE_COV_16(BASE, BITS, IDXW, BIN_IDX``1) |
| `define _DEF_TOGGLE_COV_64(BASE, BITS, IDXW, BIN_IDX) \ |
| `_DEF_TOGGLE_COV_32(BASE, BITS, IDXW, BIN_IDX``0) \ |
| `_DEF_TOGGLE_COV_32(BASE, BITS, IDXW, BIN_IDX``1) |
| `define _DEF_TOGGLE_COV_128(BASE, BITS, IDXW, BIN_IDX) \ |
| `_DEF_TOGGLE_COV_64(BASE, BITS, IDXW, BIN_IDX``0) \ |
| `_DEF_TOGGLE_COV_64(BASE, BITS, IDXW, BIN_IDX``1) |
| |
| `define DEF_GPR_TOGGLE_COV(BASE, BITS) \ |
| `_DEF_TOGGLE_COV_16(BASE, BITS, 5, 0) \ |
| `_DEF_TOGGLE_COV_16(BASE, BITS, 5, 1) |
| `define DEF_WDR_TOGGLE_COV(BASE, BITS) \ |
| `_DEF_TOGGLE_COV_128(BASE, BITS, 8, 0) \ |
| `_DEF_TOGGLE_COV_128(BASE, BITS, 8, 1) |
| `define DEF_FLAGS_TOGGLE_COV(BASE, BITS) \ |
| `_DEF_TOGGLE_COV_2(BASE, BITS, 2, 0) \ |
| `_DEF_TOGGLE_COV_2(BASE, BITS, 2, 1) |
| `define DEF_MLZ_FLAGS_TOGGLE_COV(BASE, BITS) \ |
| `_DEF_TOGGLE_COV_1(BASE, BITS, 2, 01) \ |
| `_DEF_TOGGLE_COV_2(BASE, BITS, 2, 1) |
| |
| // Macros to allow crossing the "toggle" coverpoints defined by the previous macros with the |
| // mnemonic coverpoint for some encoding schema. These work just as above and the entry points to |
| // use are DEF_*_TOGGLE_CROSS. |
| // |
| // The DEF_*_TOGGLE_COV macros above define coverpoints with names like XXX_BBBB_cp. These macros |
| // define crosses with names XXX_BBBB_cross. |
| `define _DEF_TOGGLE_CROSS_1(BASE, BIN_IDX) \ |
| BASE``_``BIN_IDX``_cross: cross mnemonic_cp, BASE``_``BIN_IDX``_cp; |
| `define _DEF_TOGGLE_CROSS_2(BASE, BIN_IDX) \ |
| `_DEF_TOGGLE_CROSS_1(BASE, BIN_IDX``0) \ |
| `_DEF_TOGGLE_CROSS_1(BASE, BIN_IDX``1) |
| `define _DEF_TOGGLE_CROSS_4(BASE, BIN_IDX) \ |
| `_DEF_TOGGLE_CROSS_2(BASE, BIN_IDX``0) \ |
| `_DEF_TOGGLE_CROSS_2(BASE, BIN_IDX``1) |
| `define _DEF_TOGGLE_CROSS_8(BASE, BIN_IDX) \ |
| `_DEF_TOGGLE_CROSS_4(BASE, BIN_IDX``0) \ |
| `_DEF_TOGGLE_CROSS_4(BASE, BIN_IDX``1) |
| `define _DEF_TOGGLE_CROSS_16(BASE, BIN_IDX) \ |
| `_DEF_TOGGLE_CROSS_8(BASE, BIN_IDX``0) \ |
| `_DEF_TOGGLE_CROSS_8(BASE, BIN_IDX``1) |
| `define _DEF_TOGGLE_CROSS_32(BASE, BIN_IDX) \ |
| `_DEF_TOGGLE_CROSS_16(BASE, BIN_IDX``0) \ |
| `_DEF_TOGGLE_CROSS_16(BASE, BIN_IDX``1) |
| `define _DEF_TOGGLE_CROSS_64(BASE, BIN_IDX) \ |
| `_DEF_TOGGLE_CROSS_32(BASE, BIN_IDX``0) \ |
| `_DEF_TOGGLE_CROSS_32(BASE, BIN_IDX``1) |
| `define _DEF_TOGGLE_CROSS_128(BASE, BIN_IDX) \ |
| `_DEF_TOGGLE_CROSS_64(BASE, BIN_IDX``0) \ |
| `_DEF_TOGGLE_CROSS_64(BASE, BIN_IDX``1) |
| |
| `define DEF_GPR_TOGGLE_CROSS(BASE) \ |
| `_DEF_TOGGLE_CROSS_16(BASE, 0) \ |
| `_DEF_TOGGLE_CROSS_16(BASE, 1) |
| `define DEF_WDR_TOGGLE_CROSS(BASE) \ |
| `_DEF_TOGGLE_CROSS_128(BASE, 0) \ |
| `_DEF_TOGGLE_CROSS_128(BASE, 1) |
| `define DEF_FLAGS_TOGGLE_CROSS(BASE) \ |
| `_DEF_TOGGLE_CROSS_2(BASE, 0) \ |
| `_DEF_TOGGLE_CROSS_2(BASE, 1) |
| `define DEF_MLZ_FLAGS_TOGGLE_CROSS(BASE) \ |
| `_DEF_TOGGLE_CROSS_1(BASE, 01) \ |
| `_DEF_TOGGLE_CROSS_2(BASE, 1) |
| |
| // A macro to define a coverpoint based on the sign of a value (assumed to be represented by an |
| // unsigned SystemVerilog expression). |
| `define DEF_SIGN_CP(NAME, VALUE, WIDTH) \ |
| NAME: coverpoint VALUE { \ |
| bins zero = {0}; \ |
| bins pos = {[1:(WIDTH'd1 << (WIDTH - 1))-1]}; \ |
| bins neg = {[WIDTH'd1 << (WIDTH - 1):$]}; \ |
| } |
| |
| // The bins for a coverpoint that checks whether a value is zero or not (assumed to be represented |
| // by an unsigned SystemVerilog expression). Used by DEF_NZ_CP and DEF_NZ_IF_CP. |
| `define _NZ_CP_BINS { \ |
| bins zero = {0}; \ |
| bins nonzero = {[1:$]}; \ |
| } |
| |
| // A macro to define a coverpoint based on whether a value is zero or not (assumed to be |
| // represented by an unsigned SystemVerilog expression). |
| `define DEF_NZ_CP(NAME, VALUE) NAME: coverpoint VALUE `_NZ_CP_BINS |
| |
| // A macro to define a coverpoint for a condition that should be seen: EXPR should be a single bit |
| // and there's just one bin (with expected value 1'b1). |
| `define DEF_SEEN_CP(NAME, EXPR) NAME: coverpoint (EXPR) { bins seen = {1'b1}; } |
| |
| // Equivalent of DEF_NZ_CP and DEF_SEEN_CP, but which add a test to qualifies the coverpoint. |
| `define DEF_NZ_IF_CP(NAME, VALUE, TEST) NAME: coverpoint (VALUE) iff (TEST) `_NZ_CP_BINS |
| `define DEF_SEEN_IF_CP(NAME, EXPR, TEST) NAME: coverpoint (EXPR) iff (TEST) { bins seen = {1'b1}; } |
| |
| // Remap a CSR index to an internal "coverage" index. This function avoids having to duplicate the |
| // list of CSRs below and is also an easy way to explicitly track invalid CSRs explicitly |
| // (SystemVerilog doesn't provide a helpful "catch anything else" bin because 'default' doesn't |
| // get included in crosses). |
| // |
| // Use it by calling DEF_CSR_CP, which uses remap_csr to map to bins and then undoes the mapping |
| // again (now that all invalid CSRs have been squashed together) to give decent bin names. |
| function int remap_csr(logic [11:0] csr_idx); |
| case (csr_idx) |
| 12'h7c0: return 0; // FG0 |
| 12'h7c1: return 1; // FG1 |
| 12'h7c8: return 2; // FLAGS |
| 12'h7d0: return 3; // MOD0 |
| 12'h7d1: return 4; // MOD1 |
| 12'h7d2: return 5; // MOD2 |
| 12'h7d3: return 6; // MOD3 |
| 12'h7d4: return 7; // MOD4 |
| 12'h7d5: return 8; // MOD5 |
| 12'h7d6: return 9; // MOD6 |
| 12'h7d7: return 10; // MOD7 |
| 12'hfc8: return 11; // RND_PREFETCH |
| 12'hfc0: return 12; // RND |
| 12'hfc1: return 13; // URND |
| default: return -1; // (invalid) |
| endcase |
| endfunction |
| |
| `define DEF_CSR_CP(NAME, EXPR) \ |
| NAME: coverpoint (remap_csr(EXPR)) { \ |
| bins fg0 = {0}; \ |
| bins fg1 = {1}; \ |
| bins flags = {2}; \ |
| bins mod0 = {3}; \ |
| bins mod1 = {4}; \ |
| bins mod2 = {5}; \ |
| bins mod3 = {6}; \ |
| bins mod4 = {7}; \ |
| bins mod5 = {8}; \ |
| bins mod6 = {9}; \ |
| bins mod7 = {10}; \ |
| bins rnd_prefetch = {11}; \ |
| bins rnd = {12}; \ |
| bins urnd = {13}; \ |
| bins invalid = {-1}; \ |
| illegal_bins bad = default; \ |
| } |
| |
| // An equivalent of remap_csr / DEF_CSR_CP, but specialized to WSRs. |
| function int remap_wsr(logic [7:0] wsr_idx); |
| case (wsr_idx) |
| 8'h00: return 0; // MOD |
| 8'h01: return 1; // RND |
| 8'h02: return 2; // URND |
| 8'h03: return 3; // ACC |
| 8'h04: return 4; // KEY_S0_L |
| 8'h05: return 5; // KEY_S0_H |
| 8'h06: return 6; // KEY_S1_L |
| 8'h07: return 7; // KEY_S1_H |
| default: return -1; // (invalid) |
| endcase |
| endfunction |
| |
| `define DEF_WSR_CP(NAME, EXPR) \ |
| NAME: coverpoint (remap_wsr(EXPR)) { \ |
| bins mod = {0}; \ |
| bins rnd = {1}; \ |
| bins urnd = {2}; \ |
| bins acc = {3}; \ |
| bins key_s0_l = {4}; \ |
| bins key_s0_h = {5}; \ |
| bins key_s1_l = {6}; \ |
| bins key_s1_h = {7}; \ |
| bins invalid = {-1}; \ |
| illegal_bins bad = default; \ |
| } |
| |
| // State tracking |
| |
| // For the LOAD_CHECKSUM CSR, we want to see the following sequence: write the CSR; perform at |
| // least one memory update; read the CSR. It's a little awkward to encode this with SystemVerilog |
| // functional coverage, so we cheat and put the temporal logic into a little "fsm state" here. |
| // This is updated in on_ext_csr_access() (when accessing the right CSR) and in on_mem_write. |
| typedef enum { |
| WUR_IDLE, |
| WUR_WRITTEN_CSR, // We've seen a write to the LOAD_CHECKSUM CSR |
| WUR_UPDATED_MEM // And now we've seen a write that updates memory |
| } load_checksum_write_upd_read_e; |
| |
| load_checksum_write_upd_read_e wur_state; |
| |
| // For some CSRs, we want to see a write to the CSR in each operational state, followed by a read |
| // before the next write (although it's ok if we've changed operational state since). To track |
| // this, we keep an associative array keyed by CSR name whose value is the last operational state |
| // where we've seen a write that changes the value. If there is a value at the name when we read |
| // the CSR, we sample the ext_csr_wr_operational_state_cg covergroup. |
| operational_state_e last_write_state[csr_str_t]; |
| |
| // To track "error promotion" based on the CTRL register, we want to see each software error cause |
| // a fatal alert. One way to do this is to run a test, check we're in the LOCKED state, then look |
| // at ERR_BITS and see just one SW error, then look at FATAL_ALERT_CAUSE and see that only the |
| // FATAL_SOFTWARE bit is set. |
| // |
| // Of course, this is a bit fiddly to track because there are several things that have to happen |
| // in order. This variable gets set on each read of ERR_BITS and cleared on reset or on a change |
| // of operational state. The idea is that if we read FATAL_ALERT_CAUSE and see only FATAL_SOFTWARE |
| // set then we can sample the promoted_err_cg covergroup, tracking which SW error was promoted. |
| // Clearing when when changing operational state makes sure that we really have seen nonzero |
| // ERR_BITS since the last operation finished. |
| logic [31:0] last_err_bits = 0; |
| |
| // The last mnemonic that we saw. Used to implement the pairwise mnemonic covergroup. Gets |
| // initialised to 0 (not a valid mnemonic!) |
| mnem_str_t last_mnem = '0; |
| |
| // Non-core covergroups ////////////////////////////////////////////////////// |
| |
| // CMD external CSR |
| covergroup ext_csr_cmd_cg |
| with function sample(otbn_pkg::cmd_e value, |
| access_e access_type, |
| operational_state_e state); |
| |
| // Expect to see each genuine command, plus at least one bogus value |
| cmd_cp: coverpoint {value} { |
| bins CmdExecute = {otbn_pkg::CmdExecute}; |
| bins CmdSecWipeDmem = {otbn_pkg::CmdSecWipeDmem}; |
| bins CmdSecWipeImem = {otbn_pkg::CmdSecWipeImem}; |
| bins BogusCmd = {[0:$]} with (!(item inside {otbn_pkg::CmdExecute, |
| otbn_pkg::CmdSecWipeDmem, |
| otbn_pkg::CmdSecWipeImem})); |
| } |
| access_type_cp: coverpoint access_type; |
| |
| // We want to see an attempt to issue every command in every state. |
| cmd_state_cross: cross cmd_cp, state iff (access_type == AccessSoftwareWrite); |
| |
| endgroup |
| |
| // CTRL external CSR |
| covergroup ext_csr_ctrl_cg |
| with function sample(logic value, |
| access_e access_type, |
| operational_state_e state); |
| // See a read as well as a write. |
| access_type_cp: coverpoint access_type; |
| |
| // See a write of each value in idle state. |
| value_cp: coverpoint value iff ((access_type == AccessSoftwareWrite) && |
| (state == OperationalStateIdle)); |
| endgroup |
| |
| // STATUS external CSR |
| covergroup ext_csr_status_cg |
| with function sample(otbn_pkg::status_e value, |
| access_e access_type); |
| // Read each possible status value |
| status_cp: coverpoint value iff (access_type == AccessSoftwareRead); |
| |
| // See a write as well as a read. |
| access_type_cp: coverpoint access_type; |
| endgroup |
| |
| // ERR_BITS external CSR |
| covergroup ext_csr_err_bits_cg |
| with function sample(otbn_pkg::err_bits_t value, |
| logic [31:0] old_value, |
| access_e access_type, |
| operational_state_e state); |
| // We want to read every error bit at least once. |
| `define DEF_ERR_BIT_CP(NAME) \ |
| `DEF_SEEN_IF_CP(err_bits_``NAME``_cp, value.NAME, access_type == AccessSoftwareRead) |
| |
| `DEF_ERR_BIT_CP(bad_insn_addr) |
| `DEF_ERR_BIT_CP(bad_data_addr) |
| `DEF_ERR_BIT_CP(call_stack) |
| `DEF_ERR_BIT_CP(illegal_insn) |
| `DEF_ERR_BIT_CP(loop) |
| `DEF_ERR_BIT_CP(key_invalid) |
| `DEF_ERR_BIT_CP(rnd_rep_chk_fail) |
| `DEF_ERR_BIT_CP(rnd_fips_chk_fail) |
| `DEF_ERR_BIT_CP(imem_intg_violation) |
| `DEF_ERR_BIT_CP(dmem_intg_violation) |
| `DEF_ERR_BIT_CP(reg_intg_violation) |
| `DEF_ERR_BIT_CP(bus_intg_violation) |
| `DEF_ERR_BIT_CP(bad_internal_state) |
| `DEF_ERR_BIT_CP(illegal_bus_access) |
| `DEF_ERR_BIT_CP(lifecycle_escalation) |
| `DEF_ERR_BIT_CP(fatal_software) |
| |
| `undef DEF_ERR_BIT_CP |
| |
| // We want to see a read of ERR_BITS in every operational state, but don't need a full cross |
| // with all possible error values. |
| state_cp: coverpoint state iff (access_type == AccessSoftwareRead); |
| |
| // We want to track writes to ERR_BITS when the old value was nonzero. |
| `DEF_SEEN_IF_CP(clear_cp, old_value != 0, access_type == AccessSoftwareWrite) |
| |
| // Cross these writes with the operational state |
| clear_state_cross: cross clear_cp, state; |
| |
| // See both reads and writes. This is actually implied by clear_cp and state_cp, but it can't |
| // hurt to be explicit. |
| access_type_cp: coverpoint access_type; |
| endgroup |
| |
| // FATAL_ALERT_CAUSE external CSR |
| covergroup ext_csr_fatal_alert_cause_cg |
| with function sample(logic [31:0] value, |
| access_e access_type, |
| operational_state_e state); |
| |
| // We want to see every valid bit at least once. |
| `define DEF_FAC_CP(NAME, IDX) \ |
| `DEF_SEEN_IF_CP(fatal_alert_cause_``NAME``_cp, value[IDX], access_type == AccessSoftwareRead) |
| |
| `DEF_FAC_CP(imem_intg_violation, 0) |
| `DEF_FAC_CP(dmem_intg_violation, 1) |
| `DEF_FAC_CP(reg_intg_violation, 2) |
| `DEF_FAC_CP(bus_intg_violation, 3) |
| `DEF_FAC_CP(bad_internal_state, 4) |
| `DEF_FAC_CP(illegal_bus_access, 5) |
| `DEF_FAC_CP(lifecycle_escalation, 6) |
| `DEF_FAC_CP(fatal_software, 7) |
| |
| `undef DEF_FAC_CP |
| |
| // We want to see a read of FATAL_ALERT_CAUSE in every operational state, but don't need to see |
| // all possible values that could be read. |
| state_cp: coverpoint state iff (access_type == AccessSoftwareRead); |
| |
| // See a write as well as a read. |
| access_type_cp: coverpoint access_type; |
| endgroup |
| |
| // INSN_CNT external CSR |
| covergroup ext_csr_insn_cnt_cg |
| with function sample(logic [31:0] value, |
| logic [31:0] old_value, |
| access_e access_type, |
| operational_state_e state); |
| |
| // We want to see at least one non-zero value of INSN_CNT to ensure it's doing something. The |
| // actual values are cross-checked with the ISS. |
| `DEF_NZ_IF_CP(insn_cnt_cp, value, access_type == AccessSoftwareRead) |
| |
| // We want to see a read from INSN_CNT in all operational states |
| state_cp: coverpoint state iff (access_type == AccessSoftwareRead); |
| |
| // We want to track writes to INSN_CNT the old value was nonzero. |
| `DEF_SEEN_IF_CP(clear_cp, old_value != 0, access_type == AccessSoftwareWrite) |
| |
| // Cross these writes with the operational state |
| clear_state_cross: cross clear_cp, state; |
| endgroup |
| |
| // Specialized covergroup for LOAD_CHECKSUM external CSR. This is used to track the "write CSR; |
| // write mem; read CSR" sequence. See notes above load_checksum_write_upd_read_e for more |
| // information. We've seen the sequence once we call the sample function. |
| covergroup ext_csr_load_checksum_wur_cg with function sample(); |
| `DEF_SEEN_CP(wur_cp, 1'b1) |
| endgroup |
| |
| // Specialized covergroup "write then read" where we that track writes across different |
| // operational states, followed by a read (to check that the write took some effect). See note |
| // above last_write_state for details of how this works. |
| covergroup ext_csr_wr_operational_state_cg |
| with function sample(csr_str_t csr, operational_state_e wr_state); |
| |
| csr_cp: coverpoint csr { |
| `DEF_CSR_BIN(csr_load_checksum); |
| `DEF_CSR_BIN(csr_ctrl); |
| illegal_bins other = default; |
| } |
| |
| csr_state_cross: cross csr_cp, wr_state; |
| |
| endgroup |
| |
| // Specialized covergroup to track that we've seen each software error individually cause a fatal |
| // alert. This gets sampled when we read a FATAL_ALERT_CAUSE with just FATAL_SOFTWARE and is given |
| // last_err_bits (see comment above declaration of that variable for more details). |
| covergroup promoted_err_cg |
| with function sample(logic [5:0] last_err_bits); |
| |
| // We're interested in the "one-hot" values corresponding to each error that wouldn't normally |
| // be fatal. |
| err_bits_cp: coverpoint last_err_bits { |
| bins bad_data_addr = {6'h1}; |
| bins bad_insn_addr = {6'h2}; |
| bins call_stack = {6'h4}; |
| bins illegal_insn = {6'h8}; |
| bins loop = {6'h10}; |
| bins key_invalid = {6'h20}; |
| } |
| endgroup |
| |
| covergroup scratchpad_writes_cg with function sample(uvm_reg_addr_t addr); |
| // See attempted writes to the bottom and top address in the scratchpad memory |
| addr_cp: coverpoint addr { |
| bins low = {OTBN_DMEM_OFFSET + OTBN_DMEM_SIZE}; |
| bins high = {OTBN_DMEM_OFFSET + DmemSizeByte - 4}; |
| } |
| endgroup |
| |
| // Non-instruction covergroups /////////////////////////////////////////////// |
| |
| covergroup call_stack_cg |
| with function sample(call_stack_flags_t flags, |
| stack_fullness_e fullness); |
| |
| // There are 3 different flags in flags (two "read ports" and a "write port"). Cross them to see |
| // all 8 possible values. |
| flags_cp: coverpoint flags; |
| |
| // 3 possible values of fullness (empty, partially full, full) |
| fullness_cp: coverpoint fullness; |
| |
| // Cross push/pop behaviour with fullness of the call stack to give 8 * 3 = 24 bins. |
| flags_fullness_cross: cross flags_cp, fullness_cp; |
| |
| endgroup |
| |
| covergroup flag_write_cg |
| with function sample(bit flag_group, |
| flags_t read_data, |
| flags_t write_data); |
| |
| // The following coverpoints track writes to the different flags in the flag group that set or |
| // clear the flag, respectively. These are then crossed with fg_cp because we want to see each |
| // event with each flag group. |
| |
| fg_cp: coverpoint flag_group; |
| |
| `DEF_SEEN_CP(set_Z_cp, write_data.Z & ~read_data.Z) |
| `DEF_SEEN_CP(set_L_cp, write_data.L & ~read_data.L) |
| `DEF_SEEN_CP(set_M_cp, write_data.M & ~read_data.M) |
| `DEF_SEEN_CP(set_C_cp, write_data.C & ~read_data.C) |
| |
| `DEF_SEEN_CP(clr_Z_cp, read_data.Z & ~write_data.Z) |
| `DEF_SEEN_CP(clr_L_cp, read_data.L & ~write_data.L) |
| `DEF_SEEN_CP(clr_M_cp, read_data.M & ~write_data.M) |
| `DEF_SEEN_CP(clr_C_cp, read_data.C & ~write_data.C) |
| |
| set_Z_cross: cross fg_cp, set_Z_cp; |
| set_L_cross: cross fg_cp, set_L_cp; |
| set_M_cross: cross fg_cp, set_M_cp; |
| set_C_cross: cross fg_cp, set_C_cp; |
| |
| clr_Z_cross: cross fg_cp, clr_Z_cp; |
| clr_L_cross: cross fg_cp, clr_L_cp; |
| clr_M_cross: cross fg_cp, clr_M_cp; |
| clr_C_cross: cross fg_cp, clr_C_cp; |
| endgroup |
| |
| // This covergroup tracks each possible "Bad Internal State" fatal error |
| // cause using probes from RTL. |
| covergroup bad_internal_state_cg |
| with function sample(logic urnd_all_zero, |
| logic insn_addr_err, |
| logic scramble_state_err, |
| otbn_pkg::predec_err_t predec_err, |
| otbn_pkg::missed_gnt_t missed_gnt, |
| otbn_pkg::controller_bad_int_t controller_bad_int, |
| otbn_pkg::start_stop_bad_int_t start_stop_bad_int, |
| logic rf_base_spurious_we_err, |
| logic rf_bignum_spurious_we_err, |
| logic ext_mubi_err); |
| |
| `DEF_SEEN_CP(alu_bignum_predec_err, predec_err.alu_bignum_err) |
| `DEF_SEEN_CP(mac_bignum_predec_err, predec_err.mac_bignum_err) |
| `DEF_SEEN_CP(ispr_bignum_predec_err, predec_err.ispr_bignum_err) |
| `DEF_SEEN_CP(controller_predec_err, predec_err.controller_err) |
| `DEF_SEEN_CP(rf_predec_err, predec_err.rf_err) |
| `DEF_SEEN_CP(rd_predec_err, predec_err.rd_err) |
| |
| `DEF_SEEN_CP(spr_urnd_acks_cp, start_stop_bad_int.spr_urnd_acks) |
| `DEF_SEEN_CP(spr_rnd_acks_cp, start_stop_bad_int.spr_rnd_acks) |
| `DEF_SEEN_CP(spr_secwipe_reqs_cp, start_stop_bad_int.spr_secwipe_reqs) |
| `DEF_SEEN_CP(mubi_rma_err_cp, start_stop_bad_int.mubi_rma_err) |
| `DEF_SEEN_CP(mubi_urnd_err_cp, start_stop_bad_int.mubi_urnd_err) |
| `DEF_SEEN_CP(start_stop_state_err_cp, start_stop_bad_int.state_err) |
| |
| `DEF_SEEN_CP(loop_hw_cnt_err_cp, controller_bad_int.loop_hw_cnt_err) |
| `DEF_SEEN_CP(loop_hw_stack_cnt_err_cp, controller_bad_int.loop_hw_stack_cnt_err) |
| `DEF_SEEN_CP(loop_hw_intg_err_cp, controller_bad_int.loop_hw_intg_err) |
| `DEF_SEEN_CP(rf_base_call_stack_err_cp, controller_bad_int.rf_base_call_stack_err) |
| `DEF_SEEN_CP(spr_secwipe_acks_cp, controller_bad_int.spr_secwipe_acks) |
| `DEF_SEEN_CP(controller_state_err_cp, controller_bad_int.state_err) |
| `DEF_SEEN_CP(controller_mubi_err_cp, controller_bad_int.controller_mubi_err) |
| |
| `DEF_SEEN_CP(imem_gnt_missed_err_cp, missed_gnt.imem_gnt_missed_err) |
| `DEF_SEEN_CP(dmem_gnt_missed_err_cp, missed_gnt.dmem_gnt_missed_err) |
| |
| `DEF_SEEN_CP(urnd_all_zero_cp, urnd_all_zero) |
| `DEF_SEEN_CP(insn_addr_err_cp, insn_addr_err) |
| `DEF_SEEN_CP(scramble_state_err_cp, scramble_state_err) |
| `DEF_SEEN_CP(rf_base_spurious_we_err_cp, rf_base_spurious_we_err) |
| `DEF_SEEN_CP(rf_bignum_spurious_we_err_cp, rf_bignum_spurious_we_err) |
| `DEF_SEEN_CP(ext_mubi_err_cp, ext_mubi_err) |
| |
| endgroup |
| |
| // This covergroup tracks each possible "Internal Integrity Errors" with probes from RTL. |
| covergroup internal_intg_err_cg with function sample(otbn_pkg::internal_intg_err_t intg_err); |
| |
| `DEF_SEEN_CP(rf_base_intg_err_cp, intg_err.rf_base_intg_err) |
| `DEF_SEEN_CP(rf_bignum_intg_err_cp, intg_err.rf_bignum_intg_err) |
| `DEF_SEEN_CP(mod_ispr_intg_err_cp, intg_err.mod_ispr_intg_err) |
| `DEF_SEEN_CP(acc_ispr_intg_err_cp, intg_err.acc_ispr_intg_err) |
| `DEF_SEEN_CP(loop_stack_addr_intg_err_cp, intg_err.loop_stack_addr_intg_err) |
| `DEF_SEEN_CP(insn_fetch_intg_err_cp, intg_err.insn_fetch_intg_err) |
| |
| endgroup |
| |
| // This covergroup tracks straight-line instructions (i.e., instructions that do not |
| // branch or jump) at the top of instruction memory (i.e., at the highest possible |
| // address). |
| covergroup insn_addr_cg with function sample(mnem_str_t mnemonic, logic [31:0] insn_addr); |
| `DEF_SEEN_CP(str_insn_at_top_cp, (insn_addr == ImemSizeByte - 4) && |
| !(mnemonic inside {mnem_beq, mnem_bne, mnem_jal, mnem_jalr, |
| mnem_ecall, mnem_loop, mnem_loopi})) |
| |
| endgroup |
| |
| // Pairwise instructions ///////////////////////////////////////////////////// |
| |
| covergroup pairwise_insn_cg with function sample(mnem_str_t last, mnem_str_t cur); |
| last_cp: coverpoint last { |
| `DEF_MNEM_BINS_EXCEPT_ECALL |
| illegal_bins other = default; |
| } |
| cur_cp: coverpoint cur { |
| `DEF_MNEM_BINS_EXCEPT_ECALL |
| `DEF_MNEM_BIN(mnem_ecall); |
| `DEF_MNEM_BIN(mnem_dummy); |
| `DEF_MNEM_BIN(mnem_question_mark); |
| illegal_bins other = default; |
| } |
| pair_cross: cross last_cp, cur_cp; |
| endgroup |
| |
| // Per-encoding covergroups ////////////////////////////////////////////////// |
| covergroup enc_bna_cg |
| with function sample(mnem_str_t mnemonic, |
| logic [31:0] insn_data, |
| logic [255:0] wdr_operand_a, |
| logic [255:0] wdr_operand_b, |
| flags_t flags_write_data [2], |
| logic [255:0] wdr_write_data); |
| |
| mnemonic_cp: coverpoint mnemonic { |
| `DEF_MNEM_BIN(mnem_bn_and); |
| `DEF_MNEM_BIN(mnem_bn_or); |
| `DEF_MNEM_BIN(mnem_bn_xor); |
| illegal_bins other = default; |
| } |
| |
| // The shifted version of wdr_operand_b is nonzero |
| `DEF_SEEN_CP(nz_shifted_cp, |
| 0 != (insn_data[30] ? |
| (wdr_operand_b >> {insn_data[29:25], 3'b0}) : |
| (wdr_operand_b << {insn_data[29:25], 3'b0}))) |
| |
| sb_cp: coverpoint insn_data[29:25] { bins extremes[] = {'0, '1}; } |
| st_cp: coverpoint insn_data[30]; |
| fg_cp: coverpoint insn_data[31]; |
| // Crossing st_cp, sb_cp and nz_shifted_cp means that we see extremal values of shift in both |
| // directions, restricted to cases where the result is nonzero (so the shift actually did |
| // something). |
| `DEF_MNEM_CROSS3(st, sb, nz_shifted) |
| `DEF_MNEM_CROSS(fg) |
| |
| `DEF_WDR_TOGGLE_COV(wrs1, wdr_operand_a) |
| `DEF_WDR_TOGGLE_COV(wrs2, wdr_operand_b) |
| `DEF_WDR_TOGGLE_CROSS(wrs1) |
| `DEF_WDR_TOGGLE_CROSS(wrs2) |
| |
| // BNA instructions can write the M, L and Z flags, but do not affect the carry flag (bit 0 in |
| // the flags_t struct). |
| `DEF_MLZ_FLAGS_TOGGLE_COV(flags, flags_write_data[insn_data[31]]) |
| `DEF_MLZ_FLAGS_TOGGLE_CROSS(flags) |
| |
| // Toggle coverage of the output result |
| `DEF_WDR_TOGGLE_COV(wrd, wdr_write_data) |
| `DEF_WDR_TOGGLE_CROSS(wrd) |
| endgroup |
| |
| covergroup enc_bnaf_cg |
| with function sample(mnem_str_t mnemonic, |
| logic [31:0] insn_data, |
| logic [255:0] wdr_operand_a, |
| logic [255:0] wdr_operand_b, |
| flags_t flags_write_data [2]); |
| |
| mnemonic_cp: coverpoint mnemonic { |
| `DEF_MNEM_BIN(mnem_bn_add); |
| `DEF_MNEM_BIN(mnem_bn_addc); |
| `DEF_MNEM_BIN(mnem_bn_sub); |
| `DEF_MNEM_BIN(mnem_bn_subb); |
| illegal_bins other = default; |
| } |
| |
| // The shifted version of wdr_operand_b is nonzero |
| `DEF_SEEN_CP(nz_shifted_cp, |
| 0 != (insn_data[30] ? |
| (wdr_operand_b >> {insn_data[29:25], 3'b0}) : |
| (wdr_operand_b << {insn_data[29:25], 3'b0}))) |
| |
| sb_cp: coverpoint insn_data[29:25] { bins extremes[] = {'0, '1}; } |
| st_cp: coverpoint insn_data[30]; |
| fg_cp: coverpoint insn_data[31]; |
| // Crossing st_cp, sb_cp and nz_shifted_cp means that we see extremal values of shift in both |
| // directions, restricted to cases where the result is nonzero (so the shift actually did |
| // something). |
| `DEF_MNEM_CROSS3(st, sb, nz_shifted) |
| `DEF_MNEM_CROSS(fg) |
| |
| `DEF_WDR_TOGGLE_COV(wrs1, wdr_operand_a) |
| `DEF_WDR_TOGGLE_COV(wrs2, wdr_operand_b) |
| `DEF_WDR_TOGGLE_CROSS(wrs1) |
| `DEF_WDR_TOGGLE_CROSS(wrs2) |
| |
| `DEF_FLAGS_TOGGLE_COV(flags, flags_write_data[insn_data[31]]) |
| `DEF_FLAGS_TOGGLE_CROSS(flags) |
| |
| // This checks for a nonzero right shift where the top bit of wrs2 is set (ensuring we do a |
| // logical shift, not an arithmetic shift). |
| `DEF_SEEN_CP(srl_cp, |
| (insn_data[29:25] != 0) && insn_data[30] && wdr_operand_b[255]) |
| `DEF_MNEM_CROSS(srl) |
| endgroup |
| |
| covergroup enc_bnai_cg |
| with function sample(mnem_str_t mnemonic, |
| logic [31:0] insn_data, |
| logic [255:0] wdr_operand_a, |
| flags_t flags_write_data [2]); |
| |
| mnemonic_cp: coverpoint mnemonic { |
| `DEF_MNEM_BIN(mnem_bn_addi); |
| `DEF_MNEM_BIN(mnem_bn_subi); |
| illegal_bins other = default; |
| } |
| |
| imm_cp: coverpoint insn_data[29:20] { bins extremes[] = {'0, '1}; } |
| fg_cp: coverpoint insn_data[31]; |
| `DEF_MNEM_CROSS(imm) |
| `DEF_MNEM_CROSS(fg) |
| |
| `DEF_WDR_TOGGLE_COV(wrs, wdr_operand_a) |
| `DEF_WDR_TOGGLE_CROSS(wrs) |
| |
| `DEF_FLAGS_TOGGLE_COV(flags, flags_write_data[insn_data[31]]) |
| `DEF_FLAGS_TOGGLE_CROSS(flags) |
| endgroup |
| |
| covergroup enc_bnam_cg |
| with function sample(mnem_str_t mnemonic, |
| logic [31:0] insn_data, |
| logic [255:0] wdr_operand_a, |
| logic [255:0] wdr_operand_b); |
| |
| mnemonic_cp: coverpoint mnemonic { |
| `DEF_MNEM_BIN(mnem_bn_addm); |
| `DEF_MNEM_BIN(mnem_bn_subm); |
| illegal_bins other = default; |
| } |
| |
| `DEF_WDR_TOGGLE_COV(wrs1, wdr_operand_a) |
| `DEF_WDR_TOGGLE_COV(wrs2, wdr_operand_b) |
| `DEF_WDR_TOGGLE_CROSS(wrs1) |
| `DEF_WDR_TOGGLE_CROSS(wrs2) |
| endgroup |
| |
| covergroup enc_bnan_cg |
| with function sample(mnem_str_t mnemonic, |
| logic [31:0] insn_data, |
| logic [255:0] wdr_operand_a, |
| flags_t flags_write_data [2], |
| logic [255:0] wdr_write_data); |
| |
| mnemonic_cp: coverpoint mnemonic { |
| `DEF_MNEM_BIN(mnem_bn_not); |
| illegal_bins other = default; |
| } |
| |
| // The shifted version of wdr_operand_a is nonzero |
| `DEF_SEEN_CP(nz_shifted_cp, |
| 0 != (insn_data[30] ? |
| (wdr_operand_a >> {insn_data[29:25], 3'b0}) : |
| (wdr_operand_a << {insn_data[29:25], 3'b0}))) |
| |
| sb_cp: coverpoint insn_data[29:25] { bins extremes[] = {'0, '1}; } |
| st_cp: coverpoint insn_data[30]; |
| fg_cp: coverpoint insn_data[31]; |
| // Crossing st_cp, sb_cp and nz_shifted_cp means that we see extremal values of shift in both |
| // directions, restricted to cases where the result is nonzero (so the shift actually did |
| // something). |
| `DEF_MNEM_CROSS3(st, sb, nz_shifted) |
| |
| // Toggle coverage of the input result |
| `DEF_WDR_TOGGLE_COV(wrs, wdr_operand_a) |
| |
| // BN.NOT can write the M, L and Z flags, but does not affect the carry flag (bit 0 in the |
| // flags_t struct). |
| `DEF_MLZ_FLAGS_TOGGLE_COV(flags, flags_write_data[insn_data[31]]) |
| |
| // Toggle coverage of the output result |
| `DEF_WDR_TOGGLE_COV(wrd, wdr_write_data) |
| endgroup |
| |
| covergroup enc_bnaq_cg |
| with function sample(mnem_str_t mnemonic, |
| logic [31:0] insn_data, |
| logic [255:0] wdr_operand_a, |
| logic [255:0] wdr_operand_b); |
| |
| // Used for BN.MULQACC |
| mnemonic_cp: coverpoint mnemonic { |
| `DEF_MNEM_BIN(mnem_bn_mulqacc); |
| illegal_bins other = default; |
| } |
| |
| za_cp: coverpoint insn_data[12]; |
| shift_cp: coverpoint insn_data[14:13] { bins extremes[] = {'0, '1}; } |
| q1_cp: coverpoint insn_data[26:25] { bins extremes[] = {'0, '1}; } |
| q2_cp: coverpoint insn_data[28:27] { bins extremes[] = {'0, '1}; } |
| |
| qwsel_cross: cross q1_cp, q2_cp; |
| |
| `DEF_WDR_TOGGLE_COV(wrs1, wdr_operand_a) |
| `DEF_WDR_TOGGLE_COV(wrs2, wdr_operand_b) |
| endgroup |
| |
| covergroup enc_bnaqs_cg |
| with function sample(mnem_str_t mnemonic, |
| logic [31:0] insn_data, |
| logic [255:0] wdr_operand_a, |
| logic [255:0] wdr_operand_b, |
| flags_t flags_write_data [2]); |
| |
| // Used for BN.MULQACC.SO |
| mnemonic_cp: coverpoint mnemonic { |
| `DEF_MNEM_BIN(mnem_bn_mulqacc_so); |
| illegal_bins other = default; |
| } |
| |
| za_cp: coverpoint insn_data[12]; |
| shift_cp: coverpoint insn_data[14:13] { bins extremes[] = {'0, '1}; } |
| q1_cp: coverpoint insn_data[26:25] { bins extremes[] = {'0, '1}; } |
| q2_cp: coverpoint insn_data[28:27] { bins extremes[] = {'0, '1}; } |
| dh_cp: coverpoint insn_data[29]; |
| fg_cp: coverpoint insn_data[31]; |
| |
| qwsel_cross: cross q1_cp, q2_cp; |
| |
| `DEF_WDR_TOGGLE_COV(wrs1, wdr_operand_a) |
| `DEF_WDR_TOGGLE_COV(wrs2, wdr_operand_b) |
| |
| // BN.MULQACC.SO can write the M, L and Z flags, but does not affect the carry flag (bit 0 in |
| // the flags_t struct). |
| `DEF_MLZ_FLAGS_TOGGLE_COV(flags, flags_write_data[insn_data[31]]) |
| |
| // Cross the M, L, Z flag coverpoints above with wrd_hwsel (the "dh" field) |
| flags_01_cross: cross dh_cp, flags_01_cp; |
| flags_10_cross: cross dh_cp, flags_10_cp; |
| flags_11_cross: cross dh_cp, flags_11_cp; |
| endgroup |
| |
| covergroup enc_bnaqw_cg |
| with function sample(mnem_str_t mnemonic, |
| logic [31:0] insn_data, |
| logic [255:0] wdr_operand_a, |
| logic [255:0] wdr_operand_b, |
| flags_t flags_write_data [2]); |
| |
| // Used for BN.MULQACC.WO |
| mnemonic_cp: coverpoint mnemonic { |
| `DEF_MNEM_BIN(mnem_bn_mulqacc_wo); |
| illegal_bins other = default; |
| } |
| |
| za_cp: coverpoint insn_data[12]; |
| shift_cp: coverpoint insn_data[14:13] { bins extremes[] = {'0, '1}; } |
| q1_cp: coverpoint insn_data[26:25] { bins extremes[] = {'0, '1}; } |
| q2_cp: coverpoint insn_data[28:27] { bins extremes[] = {'0, '1}; } |
| fg_cp: coverpoint insn_data[31]; |
| |
| qwsel_cross: cross q1_cp, q2_cp; |
| |
| `DEF_WDR_TOGGLE_COV(wrs1, wdr_operand_a) |
| `DEF_WDR_TOGGLE_COV(wrs2, wdr_operand_b) |
| |
| // BN.MULQACC.WO can write the M, L and Z flags, but does not affect the carry flag (bit 0 in |
| // the flags_t struct). |
| `DEF_MLZ_FLAGS_TOGGLE_COV(flags, flags_write_data[insn_data[31]]) |
| endgroup |
| |
| covergroup enc_bnc_cg |
| with function sample(mnem_str_t mnemonic, |
| logic [31:0] insn_data, |
| logic [255:0] wdr_operand_a, |
| logic [255:0] wdr_operand_b, |
| flags_t flags_write_data [2]); |
| |
| mnemonic_cp: coverpoint mnemonic { |
| `DEF_MNEM_BIN(mnem_bn_cmp); |
| `DEF_MNEM_BIN(mnem_bn_cmpb); |
| illegal_bins other = default; |
| } |
| |
| // The shifted version of wdr_operand_b is nonzero |
| `DEF_SEEN_CP(nz_shifted_cp, |
| 0 != (insn_data[30] ? |
| (wdr_operand_b >> {insn_data[29:25], 3'b0}) : |
| (wdr_operand_b << {insn_data[29:25], 3'b0}))) |
| |
| sb_cp: coverpoint insn_data[29:25] { bins extremes[] = {'0, '1}; } |
| st_cp: coverpoint insn_data[30]; |
| fg_cp: coverpoint insn_data[31]; |
| // Crossing st_cp, sb_cp and nz_shifted_cp means that we see extremal values of shift in both |
| // directions, restricted to cases where the result is nonzero (so the shift actually did |
| // something). |
| `DEF_MNEM_CROSS3(st, sb, nz_shifted) |
| `DEF_MNEM_CROSS(fg) |
| |
| `DEF_WDR_TOGGLE_COV(wrs1, wdr_operand_a) |
| `DEF_WDR_TOGGLE_COV(wrs2, wdr_operand_b) |
| `DEF_WDR_TOGGLE_CROSS(wrs1) |
| `DEF_WDR_TOGGLE_CROSS(wrs2) |
| |
| `DEF_FLAGS_TOGGLE_COV(flags, flags_write_data[insn_data[31]]) |
| `DEF_FLAGS_TOGGLE_CROSS(flags) |
| |
| // This checks for a nonzero right shift where the top bit of wrs2 is set (ensuring we do a |
| // logical shift, not an arithmetic shift). |
| `DEF_SEEN_CP(srl_cp, |
| (insn_data[29:25] != 0) && insn_data[30] && wdr_operand_b[255]) |
| `DEF_MNEM_CROSS(srl) |
| endgroup |
| |
| covergroup enc_bnmov_cg |
| with function sample(mnem_str_t mnemonic, |
| logic [31:0] insn_data, |
| logic [255:0] wdr_operand_a); |
| |
| mnemonic_cp: coverpoint mnemonic { |
| `DEF_MNEM_BIN(mnem_bn_mov); |
| illegal_bins other = default; |
| } |
| |
| `DEF_WDR_TOGGLE_COV(wrs, wdr_operand_a) |
| endgroup |
| |
| covergroup enc_bnmovr_cg |
| with function sample(mnem_str_t mnemonic, |
| logic [31:0] insn_data, |
| logic [31:0] gpr_operand_a, |
| logic [31:0] gpr_operand_b); |
| |
| mnemonic_cp: coverpoint mnemonic { |
| `DEF_MNEM_BIN(mnem_bn_movr); |
| illegal_bins other = default; |
| } |
| |
| grd_inc_cp: coverpoint insn_data[7]; |
| grs_inc_cp: coverpoint insn_data[9]; |
| // See all possible combinations of grd_inc and grs_inc (including the illegal one where both are set) |
| inc_cross: cross grd_inc_cp, grs_inc_cp; |
| |
| `DEF_GPR_CP(grs_cp, 19:15) |
| `DEF_GPR_CP(grd_cp, 24:20) |
| `DEF_MNEM_CROSS2(grs, grd) |
| |
| `DEF_GPR_TOGGLE_COV(grs, gpr_operand_a) |
| `DEF_GPR_TOGGLE_COV(grd, gpr_operand_b) |
| |
| big_grd_cp: coverpoint (gpr_operand_a >= 32); |
| big_grs_cp: coverpoint (gpr_operand_b >= 32); |
| big_gpr_cross: cross big_grd_cp, big_grs_cp; |
| endgroup |
| |
| covergroup enc_bnr_cg |
| with function sample(mnem_str_t mnemonic, |
| logic [31:0] insn_data, |
| logic [255:0] wdr_operand_a, |
| logic [255:0] wdr_operand_b); |
| |
| mnemonic_cp: coverpoint mnemonic { |
| `DEF_MNEM_BIN(mnem_bn_rshi); |
| illegal_bins other = default; |
| } |
| |
| imm_cp: coverpoint {insn_data[31:25], insn_data[14]} { bins extremes[] = {'0, '1}; } |
| |
| `DEF_WDR_TOGGLE_COV(wrs1, wdr_operand_a) |
| `DEF_WDR_TOGGLE_COV(wrs2, wdr_operand_b) |
| endgroup |
| |
| covergroup enc_bns_cg |
| with function sample(mnem_str_t mnemonic, |
| logic [31:0] insn_data, |
| logic [255:0] wdr_operand_a, |
| logic [255:0] wdr_operand_b, |
| flags_t flags_read_data[2]); |
| |
| mnemonic_cp: coverpoint mnemonic { |
| `DEF_MNEM_BIN(mnem_bn_sel); |
| illegal_bins other = default; |
| } |
| |
| // Note: We cover each of the 4 flags here, not just the extremes |
| flag_cp: coverpoint insn_data[26:25]; |
| fg_cp: coverpoint insn_data[31]; |
| |
| `DEF_WDR_TOGGLE_COV(wrs1, wdr_operand_a) |
| `DEF_WDR_TOGGLE_COV(wrs2, wdr_operand_b) |
| |
| flag_value_cp: coverpoint flags_read_data[insn_data[31]][insn_data[26:25]]; |
| flag_cross: cross fg_cp, flag_cp, flag_value_cp; |
| endgroup |
| |
| covergroup enc_bnxid_cg |
| with function sample(mnem_str_t mnemonic, |
| logic [31:0] insn_data, |
| logic [31:0] gpr_operand_a, |
| logic [31:0] gpr_operand_b); |
| |
| mnemonic_cp: coverpoint mnemonic { |
| `DEF_MNEM_BIN(mnem_bn_lid); |
| `DEF_MNEM_BIN(mnem_bn_sid); |
| illegal_bins other = default; |
| } |
| |
| incd_cp: coverpoint insn_data[7]; |
| inc1_cp: coverpoint insn_data[8]; |
| off_cp: coverpoint {insn_data[31:25], insn_data[11:9]} { bins extremes[] = {'0, '1}; } |
| // See all possible combinations of incd and inc1 (including the illegal one where both are set) |
| `DEF_MNEM_CROSS2(incd, inc1) |
| `DEF_MNEM_CROSS(off) |
| |
| `DEF_GPR_CP(grs1_cp, 19:15) |
| // Note: Bits 24:20 are called grd for BN.LID or grs2 for BN.SID, but both are a GPR, so can be |
| // tracked the same here. |
| `DEF_GPR_CP(grx_cp, 24:20) |
| `DEF_MNEM_CROSS2(grs1, grx) |
| |
| `DEF_GPR_TOGGLE_COV(grs1, gpr_operand_a) |
| `DEF_GPR_TOGGLE_COV(grs2, gpr_operand_b) |
| `DEF_GPR_TOGGLE_CROSS(grs1) |
| `DEF_GPR_TOGGLE_CROSS(grs2) |
| |
| // Cross the three types of GPR for GRS1 with grs1_inc |
| `DEF_MNEM_CROSS2(grs1, inc1) |
| // Cross the three types of GPR for GRD/GRS2 with grd_inc/grs2_inc |
| `DEF_MNEM_CROSS2(grx, incd) |
| endgroup |
| |
| covergroup enc_b_cg |
| with function sample(mnem_str_t mnemonic, |
| logic [31:0] insn_data, |
| logic [31:0] gpr_operand_a, |
| logic [31:0] gpr_operand_b); |
| |
| mnemonic_cp: coverpoint mnemonic { |
| `DEF_MNEM_BIN(mnem_beq); |
| `DEF_MNEM_BIN(mnem_bne); |
| illegal_bins other = default; |
| } |
| |
| off_cp: coverpoint {insn_data[31], insn_data[7], insn_data[30:25], insn_data[11:8]} { |
| bins extremes[] = {12'h800, 12'h7ff}; |
| } |
| `DEF_MNEM_CROSS(off) |
| |
| `DEF_GPR_CP(grs1_cp, 19:15) |
| `DEF_GPR_CP(grs2_cp, 24:20) |
| `DEF_MNEM_CROSS2(grs1, grs2) |
| |
| `DEF_GPR_TOGGLE_COV(grs1, gpr_operand_a) |
| `DEF_GPR_TOGGLE_COV(grs2, gpr_operand_b) |
| `DEF_GPR_TOGGLE_CROSS(grs1) |
| `DEF_GPR_TOGGLE_CROSS(grs2) |
| endgroup |
| |
| covergroup enc_ecall_cg with function sample(mnem_str_t mnemonic, logic [31:0] insn_data); |
| // Used by the ECALL instruction. Although it uses the I encoding in the tooling, it has no |
| // immediate or register operands so we give it a separate covergroup here. |
| mnemonic_cp: coverpoint mnemonic { |
| `DEF_MNEM_BIN(mnem_ecall); |
| illegal_bins other = default; |
| } |
| endgroup |
| |
| covergroup enc_i_cg |
| with function sample(mnem_str_t mnemonic, |
| logic [31:0] insn_data, |
| logic [31:0] gpr_operand_a); |
| |
| mnemonic_cp: coverpoint mnemonic { |
| `DEF_MNEM_BIN(mnem_addi); |
| `DEF_MNEM_BIN(mnem_andi); |
| `DEF_MNEM_BIN(mnem_ori); |
| `DEF_MNEM_BIN(mnem_xori); |
| `DEF_MNEM_BIN(mnem_lw); |
| `DEF_MNEM_BIN(mnem_jalr); |
| `DEF_MNEM_BIN(mnem_csrrs); |
| `DEF_MNEM_BIN(mnem_csrrw); |
| illegal_bins other = default; |
| } |
| |
| imm_cp: coverpoint insn_data[31:20] { bins extremes[] = {12'h800, 12'h7ff}; } |
| `DEF_MNEM_CROSS(imm) |
| |
| `DEF_GPR_CP(grd_cp, 11:7) |
| `DEF_GPR_CP(grs1_cp, 19:15) |
| `DEF_MNEM_CROSS2(grd, grs1) |
| |
| `DEF_GPR_TOGGLE_COV(grs1, gpr_operand_a) |
| `DEF_GPR_TOGGLE_CROSS(grs1) |
| endgroup |
| |
| covergroup enc_is_cg |
| with function sample(mnem_str_t mnemonic, |
| logic [31:0] insn_data, |
| logic [31:0] gpr_operand_a); |
| |
| // Instructions with the Is encoding |
| mnemonic_cp: coverpoint mnemonic { |
| `DEF_MNEM_BIN(mnem_slli); |
| `DEF_MNEM_BIN(mnem_srli); |
| `DEF_MNEM_BIN(mnem_srai); |
| illegal_bins other = default; |
| } |
| |
| shamt_cp: coverpoint insn_data[24:20] { bins extremes[] = {'0, '1}; } |
| `DEF_MNEM_CROSS(shamt) |
| |
| `DEF_GPR_CP(grd_cp, 11:7) |
| `DEF_GPR_CP(grs1_cp, 19:15) |
| `DEF_MNEM_CROSS2(grd, grs1) |
| |
| `DEF_GPR_TOGGLE_COV(grs1, gpr_operand_a) |
| `DEF_GPR_TOGGLE_CROSS(grs1) |
| endgroup |
| |
| covergroup enc_j_cg with function sample(mnem_str_t mnemonic, logic [31:0] insn_data); |
| mnemonic_cp: coverpoint mnemonic { |
| `DEF_MNEM_BIN(mnem_jal); |
| illegal_bins other = default; |
| } |
| |
| off_cp: coverpoint insn_data[31:12] { bins extremes[] = {20'h80000, 20'h7ffff}; } |
| |
| `DEF_GPR_CP(grd_cp, 11:7) |
| endgroup |
| |
| covergroup enc_loop_cg |
| with function sample(mnem_str_t mnemonic, |
| logic [31:0] insn_data, |
| logic [31:0] gpr_operand_a); |
| |
| // Used for LOOP encoding (just the LOOP instruction) |
| mnemonic_cp: coverpoint mnemonic { |
| `DEF_MNEM_BIN(mnem_loop); |
| illegal_bins other = default; |
| } |
| |
| sz_cp: coverpoint insn_data[31:20] { bins extremes[] = {'0, '1}; } |
| |
| `DEF_GPR_CP(grs_cp, 19:15) |
| |
| `DEF_GPR_TOGGLE_COV(grs, gpr_operand_a) |
| endgroup |
| |
| covergroup enc_loopi_cg with function sample(mnem_str_t mnemonic, logic [31:0] insn_data); |
| // Used for LOOPI encoding (just the LOOPI instruction) |
| mnemonic_cp: coverpoint mnemonic { |
| `DEF_MNEM_BIN(mnem_loopi); |
| illegal_bins other = default; |
| } |
| |
| sz_cp: coverpoint insn_data[31:20] { bins extremes[] = {'0, '1}; } |
| iterations_cp: coverpoint {insn_data[19:15], insn_data[11:7]} { bins extremes[] = {'0, '1}; } |
| endgroup |
| |
| covergroup enc_r_cg |
| with function sample(mnem_str_t mnemonic, |
| logic [31:0] insn_data, |
| logic [31:0] gpr_operand_a, |
| logic [31:0] gpr_operand_b); |
| |
| mnemonic_cp: coverpoint mnemonic { |
| `DEF_MNEM_BIN(mnem_add); |
| `DEF_MNEM_BIN(mnem_sub); |
| `DEF_MNEM_BIN(mnem_sll); |
| `DEF_MNEM_BIN(mnem_srl); |
| `DEF_MNEM_BIN(mnem_sra); |
| `DEF_MNEM_BIN(mnem_and); |
| `DEF_MNEM_BIN(mnem_or); |
| `DEF_MNEM_BIN(mnem_xor); |
| illegal_bins other = default; |
| } |
| |
| `DEF_GPR_CP(grd_cp, 11:7) |
| `DEF_GPR_CP(grs1_cp, 19:15) |
| `DEF_GPR_CP(grs2_cp, 24:20) |
| `DEF_MNEM_CROSS3(grd, grs1, grs2) |
| |
| `DEF_GPR_TOGGLE_COV(grs1, gpr_operand_a) |
| `DEF_GPR_TOGGLE_COV(grs2, gpr_operand_b) |
| `DEF_GPR_TOGGLE_CROSS(grs1) |
| `DEF_GPR_TOGGLE_CROSS(grs2) |
| endgroup |
| |
| covergroup enc_s_cg |
| with function sample(mnem_str_t mnemonic, |
| logic [31:0] insn_data, |
| logic [31:0] gpr_operand_a, |
| logic [31:0] gpr_operand_b); |
| |
| mnemonic_cp: coverpoint mnemonic { |
| `DEF_MNEM_BIN(mnem_sw); |
| illegal_bins other = default; |
| } |
| |
| off_cp: coverpoint {insn_data[31:25], insn_data[11:7]} { |
| bins extremes[] = {12'h800, 12'h7ff}; |
| } |
| |
| `DEF_GPR_CP(grs1_cp, 19:15) |
| `DEF_GPR_CP(grs2_cp, 24:20) |
| `DEF_MNEM_CROSS2(grs1, grs2) |
| |
| `DEF_GPR_TOGGLE_COV(grs1, gpr_operand_a) |
| `DEF_GPR_TOGGLE_COV(grs2, gpr_operand_b) |
| endgroup |
| |
| covergroup enc_u_cg with function sample(mnem_str_t mnemonic, logic [31:0] insn_data); |
| mnemonic_cp: coverpoint mnemonic { |
| `DEF_MNEM_BIN(mnem_lui); |
| illegal_bins other = default; |
| } |
| |
| imm_cp: coverpoint insn_data[31:12] { bins extremes[] = {'0, '1}; } |
| |
| `DEF_GPR_CP(grd_cp, 11:7) |
| endgroup |
| |
| covergroup enc_wcsr_cg with function sample(mnem_str_t mnemonic, logic [31:0] insn_data); |
| mnemonic_cp: coverpoint mnemonic { |
| `DEF_MNEM_BIN(mnem_bn_wsrr); |
| `DEF_MNEM_BIN(mnem_bn_wsrw); |
| illegal_bins other = default; |
| } |
| |
| wsr_imm_cp: coverpoint insn_data[27:20] { bins extremes[] = {'0, '1}; } |
| `DEF_MNEM_CROSS(wsr_imm) |
| |
| `DEF_WSR_CP(wsr_cp, insn_data[27:20]) |
| `DEF_MNEM_CROSS(wsr) |
| endgroup |
| |
| // Per-instruction covergroups /////////////////////////////////////////////// |
| |
| covergroup insn_addsub_cg |
| with function sample(mnem_str_t mnemonic, |
| logic [31:0] operand_a, |
| logic [31:0] operand_b); |
| mnemonic_cp: coverpoint mnemonic { |
| `DEF_MNEM_BIN(mnem_add); |
| `DEF_MNEM_BIN(mnem_sub); |
| illegal_bins other = default; |
| } |
| |
| `DEF_SIGN_CP(sign_a_cp, operand_a, 32) |
| `DEF_SIGN_CP(sign_b_cp, operand_b, 32) |
| `DEF_MNEM_CROSS2(sign_a, sign_b) |
| endgroup |
| |
| covergroup insn_addi_cg |
| with function sample(logic [31:0] insn_data, |
| logic [31:0] operand_a); |
| `DEF_SIGN_CP(sign_a_cp, operand_a, 32) |
| `DEF_SIGN_CP(sign_b_cp, insn_data[31:20], 12) |
| sign_cross: cross sign_a_cp, sign_b_cp; |
| endgroup |
| |
| covergroup insn_sll_cg |
| with function sample(logic [31:0] operand_a, |
| logic [31:0] operand_b); |
| |
| // A shift of a nonzero value by zero |
| `DEF_SEEN_CP(nz_by_z_cp, (operand_a != 0) && (operand_b == 0)) |
| // A shift of a value by 0x1f, leaving the top bit set (because the bottom bit of the value was |
| // nonzero) |
| `DEF_SEEN_CP(shift15_cp, operand_a[0] && ((operand_b & 'h1f) == 'h1f)) |
| endgroup |
| |
| covergroup insn_slli_cg |
| with function sample(logic [31:0] insn_data, |
| logic [31:0] operand_a); |
| |
| // A shift of a nonzero value by zero |
| `DEF_SEEN_CP(nz_by_z_cp, (operand_a != 0) && (insn_data[24:20] == 0)) |
| // A shift of a value by 0x1f, leaving the top bit set (because the bottom bit of the value was |
| // nonzero) |
| `DEF_SEEN_CP(shift15_cp, operand_a[0] && (insn_data[24:20] == 5'h1f)) |
| endgroup |
| |
| covergroup insn_srl_cg |
| with function sample(logic [31:0] operand_a, |
| logic [31:0] operand_b); |
| |
| // A shift of a nonzero value by zero |
| `DEF_SEEN_CP(nz_by_z_cp, (operand_a != 0) && (operand_b == 0)) |
| // A shift of a value by 0x1f, leaving the bottom bit set (because the top bit of the value was |
| // nonzero) |
| `DEF_SEEN_CP(shift15_cp, operand_a[31] && ((operand_b & 'h1f) == 'h1f)) |
| endgroup |
| |
| covergroup insn_srli_cg |
| with function sample(logic [31:0] insn_data, |
| logic [31:0] operand_a); |
| |
| // A shift of a nonzero value by zero |
| `DEF_SEEN_CP(nz_by_z_cp, (operand_a != 0) && (insn_data[24:20] == 0)) |
| // A shift of a value by 0x1f, leaving the bottom bit set (because the top bit of the value was |
| // nonzero) |
| `DEF_SEEN_CP(shift15_cp, operand_a[31] && (insn_data[24:20] == 5'h1f)) |
| endgroup |
| |
| covergroup insn_sra_cg |
| with function sample(logic [31:0] operand_a, |
| logic [31:0] operand_b); |
| |
| // A shift of a nonzero value by zero |
| `DEF_SEEN_CP(nz_by_z_cp, (operand_a != 0) && (operand_b == 0)) |
| // A shift of a value by 0x1f, leaving the bottom bit set (because the top bit of the value was |
| // nonzero) |
| `DEF_SEEN_CP(shift15_cp, operand_a[31] && ((operand_b & 'h1f) == 'h1f)) |
| endgroup |
| |
| covergroup insn_srai_cg |
| with function sample(logic [31:0] insn_data, |
| logic [31:0] operand_a); |
| |
| // A shift of a nonzero value by zero |
| `DEF_SEEN_CP(nz_by_z_cp, (operand_a != 0) && (insn_data[24:20] == 0)) |
| // A shift of a value by 0x1f, leaving the bottom bit set (because the top bit of the value was |
| // nonzero) |
| `DEF_SEEN_CP(shift15_cp, operand_a[31] && (insn_data[24:20] == 5'h1f)) |
| endgroup |
| |
| // A covergroup used for logical binary operations. |
| // |
| // For each of these operations, we want to see "toggle coverage" of the output result and expect |
| // that the output register is not x0. To handle this easily, we only call sample() when the |
| // output register is nonzero (checked with x0_cp). |
| covergroup insn_log_binop_cg |
| with function sample(mnem_str_t mnemonic, |
| logic [31:0] insn_data, |
| logic [31:0] gpr_write_data); |
| |
| mnemonic_cp: coverpoint mnemonic { |
| `DEF_MNEM_BIN(mnem_and); |
| `DEF_MNEM_BIN(mnem_andi); |
| `DEF_MNEM_BIN(mnem_or); |
| `DEF_MNEM_BIN(mnem_ori); |
| `DEF_MNEM_BIN(mnem_xor); |
| `DEF_MNEM_BIN(mnem_xori); |
| illegal_bins other = default; |
| } |
| |
| // Check we don't call sample when GRD is 0. |
| x0_cp: coverpoint insn_data[11:7] { illegal_bins x0 = {0}; } |
| |
| `DEF_GPR_TOGGLE_COV(write_data, gpr_write_data) |
| `DEF_GPR_TOGGLE_CROSS(write_data) |
| endgroup |
| |
| // A covergroup used for LW and SW. The "offset" argument to sample is the immediate offset from |
| // the instruction encoding. We extract that in the giant case statement in on_insn() so that we |
| // reuse the code for LW and SW (which encode the fields differently). |
| covergroup insn_xw_cg |
| with function sample(mnem_str_t mnemonic, |
| logic [11:0] offset, |
| logic [4:0] grs1, |
| logic [4:0] grx2, |
| logic [31:0] operand_a, |
| logic signed [31:0] addr, |
| stack_fullness_e call_stack_fullness, |
| logic call_stack_underflow); |
| |
| mnemonic_cp: coverpoint mnemonic { |
| `DEF_MNEM_BIN(mnem_lw); |
| `DEF_MNEM_BIN(mnem_sw); |
| illegal_bins other = default; |
| } |
| |
| // Load from a valid address, where grs1 is above the top of memory and a negative offset brings |
| // the load address in range. |
| `DEF_SEEN_CP(oob_base_neg_off_cp, |
| ($signed(operand_a) > DmemSizeByte) && |
| ($signed(offset) < 0) && |
| (0 <= addr) && (addr + 4 <= DmemSizeByte) && ((addr & 32'h3) == 0)) |
| `DEF_MNEM_CROSS(oob_base_neg_off) |
| |
| // Load from a valid address, where grs1 is negative and a positive offset brings the load |
| // address in range. |
| `DEF_SEEN_CP(neg_base_pos_off_cp, |
| ($signed(operand_a) < 0) && ($signed(offset) > 0) && |
| (0 <= addr) && (addr + 4 <= DmemSizeByte) && ((addr & 32'h3) == 0)) |
| `DEF_MNEM_CROSS(neg_base_pos_off) |
| |
| // Load from address zero |
| `DEF_SEEN_CP(addr0_cp, addr == 0) |
| `DEF_MNEM_CROSS(addr0) |
| |
| // Load from the top word of memory |
| `DEF_SEEN_CP(top_addr_cp, addr == DmemSizeByte - 4) |
| `DEF_MNEM_CROSS(top_addr) |
| |
| // Load from an invalid address (aligned but above the top of memory) |
| `DEF_SEEN_CP(oob_addr_cp, (addr > DmemSizeByte - 4) && ((addr & 32'h3) == 0)) |
| `DEF_MNEM_CROSS(oob_addr) |
| |
| // Load from a negative invalid address (aligned but unsigned address exceeds the top of memory) |
| `DEF_SEEN_CP(oob_addr_neg_cp, (addr < 0) && ((addr & 32'h3) == 0)) |
| `DEF_MNEM_CROSS(oob_addr_neg) |
| |
| // Load from a "barely invalid" address (the smallest aligned address that's above the top of |
| // memory) |
| `DEF_SEEN_CP(barely_oob_addr_cp, addr == DmemSizeByte) |
| `DEF_MNEM_CROSS(barely_oob_addr) |
| |
| // Cross the different possible address alignments for otherwise valid addresses |
| grs1_align_cp: coverpoint operand_a[1:0]; |
| offset_align_cp: coverpoint offset[1:0]; |
| align_cross: |
| cross mnemonic_cp, grs1_align_cp, offset_align_cp |
| iff ((0 <= addr) && (addr + 4 <= DmemSizeByte)); |
| |
| // Overflow the call stack when accessing an invalid address. Note that this is only possible |
| // for LW (since SW never pushes to the call stack). Also note that we have to compute whether |
| // this is an overflow: we'll never see the push to a full call stack on the initial cycle of |
| // the instruction because the load data comes back on the following cycle. |
| `DEF_SEEN_CP(overflow_cs_invalid_addr_cp, |
| (call_stack_fullness == StackFull) && (grx2 == 1) && |
| (addr < 0 || (addr + 4 > DmemSizeByte) || (addr & 32'h3) != 0)) |
| |
| // Underflow the call stack when accessing an invalid address. We have to be a bit careful here |
| // to make sure this is an SW instruction and we're underflowing with grs2 rather than grs1. |
| `DEF_SEEN_CP(underflow_cs_invalid_addr_cp, |
| mnemonic == mnem_sw && |
| call_stack_underflow && |
| grs1 != 5'd1 && |
| (addr < 0 || (addr + 4 > DmemSizeByte) || (addr & 32'h3) != 0)) |
| endgroup |
| |
| covergroup insn_bxx_cg |
| with function sample(mnem_str_t mnemonic, |
| logic [31:0] insn_addr, |
| logic [12:0] offset, |
| logic [31:0] operand_a, |
| logic [31:0] operand_b, |
| logic signed [31:0] tgt_addr, |
| logic at_current_loop_end_insn, |
| logic call_stack_underflow); |
| |
| mnemonic_cp: coverpoint mnemonic { |
| `DEF_MNEM_BIN(mnem_beq); |
| `DEF_MNEM_BIN(mnem_bne); |
| illegal_bins other = default; |
| } |
| |
| eq_cp: coverpoint operand_a == operand_b; |
| |
| `DEF_SIGN_CP(dir_cp, offset, 13) |
| `DEF_MNEM_CROSS2(eq, dir) |
| |
| offset_align_cp: coverpoint offset[1]; |
| `DEF_MNEM_CROSS2(eq, offset_align) |
| |
| `DEF_SEEN_CP(oob_cp, tgt_addr > ImemSizeByte) |
| `DEF_MNEM_CROSS2(eq, oob) |
| |
| `DEF_SEEN_CP(neg_cp, tgt_addr < 0) |
| `DEF_MNEM_CROSS2(eq, neg) |
| |
| at_loop_end_cp: coverpoint at_current_loop_end_insn; |
| `DEF_MNEM_CROSS2(eq, at_loop_end) |
| |
| // A branch at the end of a loop where we also underflow the call stack |
| `DEF_SEEN_CP(underflow_at_loop_end_cp, at_current_loop_end_insn && call_stack_underflow) |
| `DEF_MNEM_CROSS(underflow_at_loop_end) |
| |
| // A branch at the end of a loop that is taken and points at a bad address |
| `DEF_SEEN_CP(bad_addr_at_loop_end_cp, |
| at_current_loop_end_insn && |
| ((mnemonic == mnem_beq) ? (operand_a == operand_b) : (operand_a != operand_b)) && |
| (tgt_addr < 0 || tgt_addr > ImemSizeByte || tgt_addr[1:0] != 0)) |
| `DEF_MNEM_CROSS(bad_addr_at_loop_end) |
| endgroup |
| |
| covergroup insn_jal_cg |
| with function sample(logic [31:0] insn_addr, |
| logic [20:0] offset, |
| logic signed [31:0] tgt_addr, |
| logic at_current_loop_end_insn, |
| logic call_stack_overflow); |
| |
| `DEF_SIGN_CP(dir_cp, offset, 21) |
| offset_align_cp: coverpoint offset[1:0] { |
| bins allowed[] = {0, 2}; |
| illegal_bins other = default; |
| } |
| |
| `DEF_SEEN_CP(oob_cp, tgt_addr > ImemSizeByte) |
| `DEF_SEEN_CP(neg_cp, tgt_addr < 0) |
| `DEF_SEEN_CP(from_top_cp, insn_addr == ImemSizeByte - 4) |
| |
| at_loop_end_cp: coverpoint at_current_loop_end_insn; |
| |
| // Overflow the call stack when jumping to an invalid address |
| `DEF_SEEN_CP(overflow_and_invalid_addr_cp, |
| call_stack_overflow && |
| (tgt_addr < 0 || tgt_addr > ImemSizeByte || tgt_addr[1:0] != 0)) |
| |
| // Overflow the call stack at the end of a loop |
| `DEF_SEEN_CP(overflow_at_loop_end_cp, |
| call_stack_overflow && at_current_loop_end_insn) |
| |
| // Jump to an invalid address at the end of a loop |
| `DEF_SEEN_CP(invalid_addr_at_loop_end_cp, |
| (tgt_addr < 0 || tgt_addr > ImemSizeByte || tgt_addr[1:0] != 0) && |
| at_current_loop_end_insn) |
| |
| // Overflow the call stack while jumping to an invalid address at the end of a loop |
| `DEF_SEEN_CP(overflow_and_invalid_addr_at_loop_end_cp, |
| call_stack_overflow && |
| (tgt_addr < 0 || tgt_addr > ImemSizeByte || tgt_addr[1:0] != 0) && |
| at_current_loop_end_insn) |
| endgroup |
| |
| covergroup insn_jalr_cg |
| with function sample(logic [31:0] insn_addr, |
| logic [11:0] offset, |
| logic signed [31:0] tgt_addr, |
| logic [31:0] operand_a, |
| logic at_current_loop_end_insn, |
| logic call_stack_underflow, |
| logic call_stack_overflow); |
| |
| `DEF_SIGN_CP(off_dir_cp, offset, 12) |
| |
| offset_align_cp: coverpoint offset[1:0]; |
| base_align_cp: coverpoint operand_a[1:0]; |
| align_cross: cross offset_align_cp, base_align_cp; |
| |
| // Jump with a large base address which wraps to a valid address by adding a positive offset. |
| `DEF_SEEN_CP(pos_wrap_cp, |
| (operand_a >= ImemSizeByte) && |
| ($signed(offset) > 0) && |
| (tgt_addr <= ImemSizeByte - 4)) |
| |
| // Jump with a base address just above top of IMEM but with a negative offset to give a valid |
| // target. |
| `DEF_SEEN_CP(sub_cp, |
| (operand_a >= ImemSizeByte) && |
| ($signed(offset) < 0) && |
| (tgt_addr <= ImemSizeByte - 4)) |
| |
| // Jump with a negative offset, wrapping to give an invalid target. |
| `DEF_SEEN_CP(neg_wrap_cp, |
| (operand_a <= ImemSizeByte - 4) && |
| ($signed(offset) < 0) && |
| (tgt_addr > ImemSizeByte - 4)) |
| |
| // Jump to an aligned address above top of IMEM. |
| `DEF_SEEN_CP(oob_cp, (((tgt_addr) & 32'h3) == 0) && (tgt_addr > ImemSizeByte - 4)) |
| |
| // Jump to current address. |
| `DEF_SEEN_CP(self_cp, tgt_addr == insn_addr) |
| |
| // Jump when the current PC is the top word in IMEM. |
| `DEF_SEEN_CP(from_top_cp, insn_addr == ImemSizeByte - 4) |
| |
| // Is this jump the last instruction of the current loop? |
| at_loop_end_cp: coverpoint at_current_loop_end_insn; |
| |
| // Underflow call stack at end of loop |
| `DEF_SEEN_CP(underflow_at_loop_end_cp, call_stack_underflow && at_current_loop_end_insn) |
| |
| // Overflow call stack when jumping to an invalid address |
| `DEF_SEEN_CP(overflow_and_bad_addr_cp, |
| call_stack_overflow && |
| (tgt_addr < 0 || tgt_addr >= ImemSizeByte || tgt_addr[1:0] != 0)) |
| |
| // Overflow call stack at end of loop |
| `DEF_SEEN_CP(overflow_at_loop_end_cp, call_stack_overflow && at_current_loop_end_insn) |
| |
| // Jump to an invalid address at the end of a loop |
| `DEF_SEEN_CP(bad_addr_at_loop_end_cp, |
| (tgt_addr < 0 || tgt_addr >= ImemSizeByte || tgt_addr[1:0] != 0) && |
| at_current_loop_end_insn) |
| |
| // Overflow call stack when jumping to an invalid address at the end of a loop |
| `DEF_SEEN_CP(overflow_and_bad_addr_at_loop_end_cp, |
| call_stack_overflow && |
| (tgt_addr < 0 || tgt_addr >= ImemSizeByte || tgt_addr[1:0] != 0) && |
| at_current_loop_end_insn) |
| endgroup |
| |
| covergroup insn_csrrs_cg |
| with function sample(logic [31:0] insn_data, |
| logic [31:0] operand_a, |
| stack_fullness_e call_stack_fullness); |
| |
| `DEF_CSR_CP(csr_cp, insn_data[31:20]) |
| `DEF_NZ_CP(bits_to_set_cp, operand_a) |
| |
| csr_cross: cross csr_cp, bits_to_set_cp; |
| |
| // Underflow the call stack while accessing an invalid CSR. The RTL squashes the GPR reads in |
| // this case, so we have to figure out a call stack underflow by hand (true if the call stack |
| // was empty and grs1 is x1) |
| `DEF_SEEN_CP(underflow_with_bad_csr_cp, |
| (call_stack_fullness == StackEmpty) && (insn_data[19:15] == 5'd1) && |
| (remap_csr(insn_data[31:20]) == -1)) |
| |
| // Overflow the call stack while accessing an invalid CSR. The RTL squashes the GPR reads in |
| // this case, so we have to figure out a call stack underflow by hand (true if the call stack |
| // was full and grd is x1) |
| `DEF_SEEN_CP(overflow_with_bad_csr_cp, |
| (call_stack_fullness == StackFull) && (insn_data[11:7] == 5'd1) && |
| (remap_csr(insn_data[31:20]) == -1)) |
| endgroup |
| |
| covergroup insn_csrrw_cg |
| with function sample(logic [31:0] insn_data, |
| stack_fullness_e call_stack_fullness); |
| |
| `DEF_CSR_CP(csr_cp, insn_data[31:20]) |
| `DEF_NZ_CP(grd_cp, insn_data[11:7]) |
| |
| csr_cross: cross csr_cp, grd_cp; |
| |
| `DEF_SEEN_CP(uimp_cp, insn_data == 32'hC0001073) |
| |
| // Underflow the call stack while accessing an invalid CSR. The RTL squashes the GPR reads in |
| // this case, so we have to figure out a call stack underflow by hand (true if the call stack |
| // was empty and grs1 is x1) |
| `DEF_SEEN_CP(underflow_with_bad_csr_cp, |
| (call_stack_fullness == StackEmpty) && (insn_data[19:15] == 5'd1) && |
| (remap_csr(insn_data[31:20]) == -1)) |
| |
| // Overflow the call stack while accessing an invalid CSR. The RTL squashes the GPR reads in |
| // this case, so we have to figure out a call stack underflow by hand (true if the call stack |
| // was full and grd is x1) |
| `DEF_SEEN_CP(overflow_with_bad_csr_cp, |
| (call_stack_fullness == StackFull) && (insn_data[11:7] == 5'd1) && |
| (remap_csr(insn_data[31:20]) == -1)) |
| endgroup |
| |
| covergroup insn_loop_cg |
| with function sample(logic [31:0] insn_addr, |
| logic [31:0] insn_data, |
| logic [31:0] operand_a, |
| stack_fullness_e loop_stack_fullness, |
| logic [31:0] current_loop_end, |
| logic [31:0] bodysize, |
| logic at_loop_end, |
| logic call_stack_underflow); |
| // Extremes for iteration count |
| iterations_cp: coverpoint operand_a { bins extremes[] = {'0, '1}; } |
| |
| // Is the loop end address above the top of memory? |
| oob_end_addr_cp: coverpoint insn_addr + 4 * bodysize >= ImemSizeByte; |
| |
| // Current state of the loop stack (empty, half full, full) |
| loop_stack_fullness_cp: coverpoint loop_stack_fullness; |
| |
| // Is this loop the last instruction of the current loop? |
| at_loop_end_cp: coverpoint at_loop_end; |
| |
| // Does the loop end for this instruction match the top of a nonempty loop stack? If valid, |
| // current_loop_end is the address of the last instruction in the loop body. The last |
| // instruction of the current loop body is the current PC plus 4 for the LOOP instruction and |
| // then 4 * (bodysize - 1) for all but the last instruction of the loop body. |
| `DEF_SEEN_CP(duplicate_loop_end_cp, |
| (loop_stack_fullness != StackEmpty) && |
| (current_loop_end == insn_addr + 4 * bodysize)) |
| |
| // Underflow the call stack at the end of a loop |
| `DEF_SEEN_CP(underflow_at_loop_end_cp, at_loop_end && call_stack_underflow) |
| |
| // A zero loop count at the end of a loop |
| `DEF_SEEN_CP(zero_count_at_loop_end_cp, at_loop_end && (operand_a == 0)) |
| endgroup |
| |
| covergroup insn_loopi_cg |
| with function sample(logic [31:0] insn_addr, |
| logic [31:0] insn_data, |
| stack_fullness_e loop_stack_fullness, |
| logic [31:0] current_loop_end, |
| logic [9:0] iterations, |
| logic [31:0] bodysize, |
| logic at_loop_end); |
| |
| // Is the loop end address above the top of memory? |
| oob_end_addr_cp: coverpoint insn_addr + 4 * bodysize >= ImemSizeByte; |
| |
| // Current state of the loop stack (empty, half full, full) |
| loop_stack_fullness_cp: coverpoint loop_stack_fullness; |
| |
| // Is this loop the last instruction of the current loop? |
| at_loop_end_cp: coverpoint at_loop_end; |
| |
| // Does the loop end for this instruction match the top of a nonempty loop stack? If valid, |
| // current_loop_end is the address of the last instruction in the loop body. The last |
| // instruction of the current loop body is the current PC plus 4 for the LOOPI instruction and |
| // then 4 * (bodysize - 1) for all but the last instruction of the loop body. |
| `DEF_SEEN_CP(duplicate_loop_end_cp, |
| (loop_stack_fullness != StackEmpty) && |
| (current_loop_end == insn_addr + 4 * bodysize)) |
| |
| // A zero loop count at the end of a loop |
| `DEF_SEEN_CP(zero_count_at_loop_end_cp, at_loop_end && (iterations == 0)) |
| endgroup |
| |
| covergroup insn_bn_addc_cg |
| with function sample(logic [31:0] insn_data, |
| flags_t flags_read_data[2]); |
| |
| // Execute with both values of the carry flag for both flag groups |
| fg_cp: coverpoint insn_data[31]; |
| carry_flag_cp: coverpoint flags_read_data[insn_data[31]].C; |
| carry_cross: cross fg_cp, carry_flag_cp; |
| |
| endgroup |
| |
| covergroup insn_bn_addm_cg |
| with function sample(logic [31:0] insn_data, |
| logic [255:0] wdr_operand_a, |
| logic [255:0] wdr_operand_b, |
| logic [255:0] mod); |
| |
| // Extreme values of MOD |
| mod_cp: coverpoint mod { bins extremes[] = {'0, '1}; } |
| |
| // Sum less than MOD (so we don't do a subtraction). Here, and below, we zero-extend explicitly |
| // in the sum, which uses 257 bits internally. |
| `DEF_SEEN_CP(sum_lt_cp, |
| (mod != 0) && ({1'b0, wdr_operand_a} + {1'b0, wdr_operand_b} < {1'b0, mod})) |
| |
| // Sum exactly equals a nonzero MOD (subtracting down to zero) |
| `DEF_SEEN_CP(sum_eq_cp, |
| (mod != 0) && ({1'b0, wdr_operand_a} + {1'b0, wdr_operand_b} == {1'b0, mod})) |
| |
| // Sum is greater than a nonzero MOD, but not twice as big. |
| `DEF_SEEN_CP(sum_gt_cp, |
| (mod != 0) && |
| ({1'b0, wdr_operand_a} + {1'b0, wdr_operand_b} > {1'b0, mod}) && |
| ({1'b0, wdr_operand_a} + {1'b0, wdr_operand_b} < {mod, 1'b0})) |
| |
| // Sum is at least twice a nonzero MOD. |
| `DEF_SEEN_CP(sum_gt2_cp, |
| (mod != 0) && |
| ({1'b0, wdr_operand_a} + {1'b0, wdr_operand_b} >= {mod, 1'b0})) |
| |
| // The intermediate sum overflows 256 bits |
| `DEF_SEEN_CP(overflow_cp, |
| (({1'b0, wdr_operand_a} + {1'b0, wdr_operand_b}) >> 256) != 0) |
| |
| // Wrap after subtraction? |
| wrap_after_mod_cp: coverpoint ((({1'b0, wdr_operand_a} + {1'b0, wdr_operand_b} > {1'b0, mod}) ? |
| ({1'b0, wdr_operand_a} + {1'b0, wdr_operand_b} - {1'b0, mod}) : |
| ({1'b0, wdr_operand_a} + {1'b0, wdr_operand_b})) >> 256) != 0; |
| |
| overflow_wrap_cross: cross overflow_cp, wrap_after_mod_cp; |
| endgroup |
| |
| covergroup insn_bn_mulqaccx_cg |
| with function sample(mnem_str_t mnemonic, |
| logic [256:0] new_acc_extended); |
| |
| mnemonic_cp: coverpoint mnemonic { |
| `DEF_MNEM_BIN(mnem_bn_mulqacc); |
| `DEF_MNEM_BIN(mnem_bn_mulqacc_so); |
| `DEF_MNEM_BIN(mnem_bn_mulqacc_wo); |
| illegal_bins other = default; |
| } |
| |
| // ACC will be truncated (the sum overflowed) |
| `DEF_SEEN_CP(overflow_cp, new_acc_extended[256]) |
| `DEF_MNEM_CROSS(overflow) |
| |
| endgroup |
| |
| covergroup insn_bn_subcmpb_cg |
| with function sample(mnem_str_t mnemonic, |
| logic [31:0] insn_data, |
| flags_t flags_read_data[2]); |
| |
| mnemonic_cp: coverpoint mnemonic { |
| `DEF_MNEM_BIN(mnem_bn_subb); |
| `DEF_MNEM_BIN(mnem_bn_cmpb); |
| illegal_bins other = default; |
| } |
| |
| // Execute with both values of the carry flag (borrow here!) for both flag groups |
| fg_cp: coverpoint insn_data[31]; |
| carry_flag_cp: coverpoint flags_read_data[insn_data[31]].C; |
| `DEF_MNEM_CROSS2(fg, carry_flag) |
| |
| endgroup |
| |
| covergroup insn_bn_subm_cg |
| with function sample(logic [31:0] insn_data, |
| logic [255:0] wdr_operand_a, |
| logic [255:0] wdr_operand_b, |
| logic [255:0] mod); |
| |
| // Extreme values of MOD |
| mod_cp: coverpoint mod { bins extremes[] = {'0, '1}; } |
| |
| // A non-negative difference with a nonzero MOD. |
| `DEF_SEEN_CP(diff_nonneg_cp, |
| (mod != 0) && ({1'b0, wdr_operand_a} - {1'b0, wdr_operand_b} < {1'b1, 256'b0})) |
| |
| // Difference exactly equals a nonzero -MOD (adding back up to zero) |
| `DEF_SEEN_CP(diff_minus_mod_cp, |
| (mod != 0) && |
| ({1'b0, wdr_operand_a} - {1'b0, wdr_operand_b} + {1'b0, mod} == 0)) |
| |
| // Difference is between -MOD and -1. |
| `DEF_SEEN_CP(diff_neg_cp, |
| (mod != 0) && |
| ({1'b0, wdr_operand_a} - {1'b0, wdr_operand_b} >= {1'b1, 256'b0}) && |
| ({1'b0, wdr_operand_a} - {1'b0, wdr_operand_b} + {1'b0, mod} < {1'b1, 256'b0})) |
| |
| // Difference is less than -MOD |
| `DEF_SEEN_CP(diff_neg2_cp, |
| (mod != 0) && |
| (mod != 0) && |
| ({1'b0, wdr_operand_a} - {1'b0, wdr_operand_b} >= {1'b1, 256'b0}) && |
| ({1'b0, wdr_operand_a} - {1'b0, wdr_operand_b} + {1'b0, mod} >= {1'b1, 256'b0})) |
| endgroup |
| |
| // Used by BN.LID and BN.SID |
| covergroup insn_bn_xid_cg |
| with function sample(mnem_str_t mnemonic, |
| logic [14:0] offset, |
| logic [31:0] operand_a, |
| logic [31:0] operand_b, |
| logic signed [31:0] addr, |
| logic inc_both, |
| logic [4:0] grs1, |
| logic [4:0] grx2, |
| logic call_stack_underflow); |
| |
| mnemonic_cp: coverpoint mnemonic { |
| `DEF_MNEM_BIN(mnem_bn_lid); |
| `DEF_MNEM_BIN(mnem_bn_sid); |
| illegal_bins other = default; |
| } |
| |
| // Access a valid address, where grs1 is above the top of memory and a negative offset brings |
| // the address in range. |
| `DEF_SEEN_CP(oob_base_neg_off_cp, |
| ($signed(operand_a) > DmemSizeByte) && |
| ($signed(offset) < 0) && |
| (0 <= addr) && (addr + 32 <= DmemSizeByte) && ((addr & 32'd31) == 0)) |
| `DEF_MNEM_CROSS(oob_base_neg_off) |
| |
| // Access a valid address, where grs1 is negative and a positive offset brings the address in |
| // range. |
| `DEF_SEEN_CP(neg_base_pos_off_cp, |
| ($signed(operand_a) < 0) && |
| ($signed(offset) > 0) && |
| (0 <= addr) && (addr + 32 <= DmemSizeByte) && ((addr & 32'd31) == 0)) |
| `DEF_MNEM_CROSS(neg_base_pos_off) |
| |
| // Access address zero |
| `DEF_SEEN_CP(addr0_cp, addr == 0) |
| `DEF_MNEM_CROSS(addr0) |
| |
| // Access the top word of memory |
| `DEF_SEEN_CP(top_addr_cp, addr == DmemSizeByte - 32) |
| `DEF_MNEM_CROSS(top_addr) |
| |
| // Access an invalid address (aligned but above the top of memory) |
| `DEF_SEEN_CP(oob_addr_cp, |
| (addr > DmemSizeByte - 32) && ((addr & 32'd31) == 0)) |
| `DEF_MNEM_CROSS(oob_addr) |
| |
| // Load from a negative invalid address (aligned but unsigned address exceeds the top of memory) |
| `DEF_SEEN_CP(oob_addr_neg_cp, |
| (addr < 0) && ((addr & 32'h3) == 0)) |
| `DEF_MNEM_CROSS(oob_addr_neg) |
| |
| // Misaligned address tracking (see DV document for why we have these exact crosses) |
| addr_align_cp: coverpoint operand_a[4:0]; |
| |
| addr_align_cross: |
| cross mnemonic_cp, addr_align_cp |
| iff ((0 <= addr) && (addr + 32 <= DmemSizeByte)); |
| |
| // See operand_b >= 32. This is grs2 for BN.SID and grd for BN.LID: in either case, it causes an |
| // error. |
| `DEF_SEEN_CP(bigb_cp, operand_b >= 32) |
| `DEF_MNEM_CROSS(bigb) |
| |
| // Underflow call stack and set both increments |
| `DEF_SEEN_CP(underflow_and_inc_both_cp, call_stack_underflow && inc_both) |
| `DEF_MNEM_CROSS(underflow_and_inc_both) |
| |
| // Underflow call stack with grs1 and have a bad WDR index in *grd or *grs2 (depending on |
| // whether it's BN.LID or BN.SID). |
| `DEF_SEEN_CP(underflow_and_badb_cp, |
| call_stack_underflow && (grx2 != 5'd1) && (operand_b >= 32)) |
| `DEF_MNEM_CROSS(underflow_and_badb) |
| |
| // Underflow call stack with grd/grs2 and compute a bad address from *grs1 |
| `DEF_SEEN_CP(underflow_and_bad_addr_cp, |
| call_stack_underflow && |
| (grs1 != 5'd1) && |
| ((0 < addr) || (addr >= DmemSizeByte) || ((addr & 32'd31) != 0))) |
| `DEF_MNEM_CROSS(underflow_and_bad_addr) |
| |
| // Set both increments and have a bad WDR index in *grd/*grs2. |
| `DEF_SEEN_CP(inc_both_and_bad_wdr_cp, |
| inc_both && !call_stack_underflow && (operand_b >= 32)) |
| `DEF_MNEM_CROSS(inc_both_and_bad_wdr) |
| |
| // Set both increments and compute a bad address from *grs1 |
| `DEF_SEEN_CP(inc_both_and_bad_addr_cp, |
| inc_both && |
| !call_stack_underflow && |
| ((0 < addr) || (addr >= DmemSizeByte) || ((addr & 32'd31) != 0))) |
| `DEF_MNEM_CROSS(inc_both_and_bad_addr) |
| |
| // Have a bad WDR index and also compute a bad address |
| `DEF_SEEN_CP(bad_wdr_and_bad_addr_cp, |
| !call_stack_underflow && |
| (operand_b >= 32) && |
| ((0 < addr) || (addr >= DmemSizeByte) || ((addr & 32'd31) != 0))) |
| `DEF_MNEM_CROSS(bad_wdr_and_bad_addr) |
| |
| // Underflow call stack with grs1, set both increments, and have a bad WDR index in *grd/*grs2 |
| `DEF_SEEN_CP(underflow_and_inc_both_and_bad_wdr_cp, |
| call_stack_underflow && |
| inc_both && |
| (grx2 != 5'd1) && |
| (operand_b >= 32)) |
| `DEF_MNEM_CROSS(underflow_and_inc_both_and_bad_wdr) |
| |
| // Underflow call stack with grd/grs2, set both increments, and compute a bad address from *grs1 |
| `DEF_SEEN_CP(underflow_and_inc_both_and_bad_addr_cp, |
| call_stack_underflow && |
| inc_both && |
| (grs1 != 5'd1) && |
| ((0 < addr) || (addr >= DmemSizeByte) || ((addr & 32'd31) != 0))) |
| `DEF_MNEM_CROSS(underflow_and_inc_both_and_bad_addr) |
| |
| // Set both increments, have a bad WDR index and compute a bad address |
| `DEF_SEEN_CP(inc_both_and_bad_wdr_and_bad_addr_cp, |
| inc_both && |
| !call_stack_underflow && |
| (operand_b >= 32) && |
| ((0 < addr) || (addr >= DmemSizeByte) || ((addr & 32'd31) != 0))) |
| `DEF_MNEM_CROSS(inc_both_and_bad_wdr_and_bad_addr) |
| endgroup |
| |
| covergroup insn_bn_movr_cg |
| with function sample(logic [31:0] operand_a, |
| logic [31:0] operand_b, |
| logic inc_both, |
| logic [4:0] grs, |
| logic [4:0] grd, |
| logic call_stack_underflow); |
| |
| // Underflow call stack and set both increments |
| `DEF_SEEN_CP(underflow_and_inc_both_cp, call_stack_underflow && inc_both) |
| |
| // Underflow call stack for grs and have a bad WDR index in *grd. |
| `DEF_SEEN_CP(underflow_and_bad_grd_cp, |
| call_stack_underflow && (grd != 5'd1) && (operand_b >= 32)) |
| |
| // Underflow call stack for grd and have a bad WDR index in *grs. |
| `DEF_SEEN_CP(underflow_and_bad_grs_cp, |
| call_stack_underflow && (grs != 5'd1) && (operand_a >= 32)) |
| |
| // Set both increments and have a bad WDR index. |
| `DEF_SEEN_CP(inc_both_and_bad_wdr_cp, |
| inc_both && |
| !call_stack_underflow && |
| ((operand_a >= 32) || (operand_b >= 32))) |
| |
| // Underflow call stack for grs, setting both increments and have a bad WDR index in *grd. |
| `DEF_SEEN_CP(underflow_grs_and_inc_both_and_bad_wdr_cp, |
| call_stack_underflow && |
| inc_both && |
| (grd != 5'd1) && |
| (operand_b >= 32)) |
| |
| // Underflow call stack for grd, setting both increments and have a bad WDR index in *grs. |
| `DEF_SEEN_CP(underflow_grd_and_inc_both_and_bad_wdr_cp, |
| call_stack_underflow && |
| inc_both && |
| (grs != 5'd1) && |
| (operand_a >= 32)) |
| endgroup |
| |
| covergroup insn_bn_wsrr_cg |
| with function sample(logic [7:0] wsr_imm, logic has_sideload_key); |
| |
| // Track coverage of the key sideload WSRs specifically. Here 4 - 7 are the indices of KEY_S0_L, |
| // KEY_S0_H, KEY_S1_L and KEY_S1_H respectively. |
| key_wsr_cp: coverpoint wsr_imm { bins key_wsrs[] = {[4:7]}; } |
| |
| // Crossing key_wsr_cp with has_sideload_key asks that we see a read of each WSR both with and |
| // without a valid key. |
| key_avail_cross: cross key_wsr_cp, has_sideload_key; |
| |
| endgroup |
| |
| // A mapping from instruction name to the name of that instruction's encoding. |
| string insn_encodings[mnem_str_t]; |
| |
| function new(string name, uvm_component parent); |
| super.new(name, parent); |
| |
| ext_csr_cmd_cg = new; |
| ext_csr_ctrl_cg = new; |
| ext_csr_status_cg = new; |
| ext_csr_err_bits_cg = new; |
| ext_csr_fatal_alert_cause_cg = new; |
| ext_csr_insn_cnt_cg = new; |
| ext_csr_load_checksum_wur_cg = new; |
| ext_csr_wr_operational_state_cg = new; |
| promoted_err_cg = new; |
| scratchpad_writes_cg = new; |
| |
| bad_internal_state_cg = new; |
| internal_intg_err_cg = new; |
| insn_addr_cg = new; |
| call_stack_cg = new; |
| flag_write_cg = new; |
| pairwise_insn_cg = new; |
| |
| enc_bna_cg = new; |
| enc_bnaf_cg = new; |
| enc_bnai_cg = new; |
| enc_bnam_cg = new; |
| enc_bnan_cg = new; |
| enc_bnaq_cg = new; |
| enc_bnaqw_cg = new; |
| enc_bnaqs_cg = new; |
| enc_bnc_cg = new; |
| enc_bnmov_cg = new; |
| enc_bnmovr_cg = new; |
| enc_bnr_cg = new; |
| enc_bns_cg = new; |
| enc_bnxid_cg = new; |
| enc_b_cg = new; |
| enc_ecall_cg = new; |
| enc_i_cg = new; |
| enc_is_cg = new; |
| enc_j_cg = new; |
| enc_loop_cg = new; |
| enc_loopi_cg = new; |
| enc_r_cg = new; |
| enc_s_cg = new; |
| enc_wcsr_cg = new; |
| enc_u_cg = new; |
| |
| insn_addsub_cg = new; |
| insn_addi_cg = new; |
| insn_sll_cg = new; |
| insn_slli_cg = new; |
| insn_srl_cg = new; |
| insn_srli_cg = new; |
| insn_sra_cg = new; |
| insn_srai_cg = new; |
| insn_log_binop_cg = new; |
| insn_xw_cg = new; |
| insn_bxx_cg = new; |
| insn_jal_cg = new; |
| insn_jalr_cg = new; |
| insn_csrrs_cg = new; |
| insn_csrrw_cg = new; |
| insn_loop_cg = new; |
| insn_loopi_cg = new; |
| insn_bn_addc_cg = new; |
| insn_bn_addm_cg = new; |
| insn_bn_mulqaccx_cg = new; |
| insn_bn_subcmpb_cg = new; |
| insn_bn_subm_cg = new; |
| insn_bn_xid_cg = new; |
| insn_bn_movr_cg = new; |
| insn_bn_wsrr_cg = new; |
| |
| // Set up instruction encoding mapping |
| insn_encodings[mnem_add] = "R"; |
| insn_encodings[mnem_addi] = "I"; |
| insn_encodings[mnem_lui] = "U"; |
| insn_encodings[mnem_sub] = "R"; |
| insn_encodings[mnem_sll] = "R"; |
| insn_encodings[mnem_slli] = "Is"; |
| insn_encodings[mnem_srl] = "R"; |
| insn_encodings[mnem_srli] = "Is"; |
| insn_encodings[mnem_sra] = "R"; |
| insn_encodings[mnem_srai] = "Is"; |
| insn_encodings[mnem_and] = "R"; |
| insn_encodings[mnem_andi] = "I"; |
| insn_encodings[mnem_or] = "R"; |
| insn_encodings[mnem_ori] = "I"; |
| insn_encodings[mnem_xor] = "R"; |
| insn_encodings[mnem_xori] = "I"; |
| insn_encodings[mnem_lw] = "I"; |
| insn_encodings[mnem_sw] = "S"; |
| insn_encodings[mnem_beq] = "B"; |
| insn_encodings[mnem_bne] = "B"; |
| insn_encodings[mnem_jal] = "J"; |
| insn_encodings[mnem_jalr] = "I"; |
| insn_encodings[mnem_csrrs] = "I"; |
| insn_encodings[mnem_csrrw] = "I"; |
| insn_encodings[mnem_ecall] = "ecall"; |
| insn_encodings[mnem_loop] = "loop"; |
| insn_encodings[mnem_loopi] = "loopi"; |
| insn_encodings[mnem_bn_add] = "bnaf"; |
| insn_encodings[mnem_bn_addc] = "bnaf"; |
| insn_encodings[mnem_bn_addi] = "bnai"; |
| insn_encodings[mnem_bn_addm] = "bnam"; |
| insn_encodings[mnem_bn_mulqacc] = "bnaq"; |
| insn_encodings[mnem_bn_mulqacc_wo] = "bnaqw"; |
| insn_encodings[mnem_bn_mulqacc_so] = "bnaqs"; |
| insn_encodings[mnem_bn_sub] = "bnaf"; |
| insn_encodings[mnem_bn_subb] = "bnaf"; |
| insn_encodings[mnem_bn_subi] = "bnai"; |
| insn_encodings[mnem_bn_subm] = "bnam"; |
| insn_encodings[mnem_bn_and] = "bna"; |
| insn_encodings[mnem_bn_or] = "bna"; |
| insn_encodings[mnem_bn_not] = "bnan"; |
| insn_encodings[mnem_bn_xor] = "bna"; |
| insn_encodings[mnem_bn_rshi] = "bnr"; |
| insn_encodings[mnem_bn_sel] = "bns"; |
| insn_encodings[mnem_bn_cmp] = "bnc"; |
| insn_encodings[mnem_bn_cmpb] = "bnc"; |
| insn_encodings[mnem_bn_lid] = "bnxid"; |
| insn_encodings[mnem_bn_sid] = "bnxid"; |
| insn_encodings[mnem_bn_mov] = "bnmov"; |
| insn_encodings[mnem_bn_movr] = "bnmovr"; |
| insn_encodings[mnem_bn_wsrr] = "wcsr"; |
| insn_encodings[mnem_bn_wsrw] = "wcsr"; |
| insn_encodings[mnem_dummy] = "dummy"; |
| insn_encodings[mnem_question_mark] = "dummy"; |
| endfunction |
| |
| // Reset any internal state tracking because the DUT has just seen a reset |
| function void on_reset(); |
| wur_state = WUR_IDLE; |
| last_write_state.delete(); |
| last_err_bits = 0; |
| last_mnem = '0; |
| endfunction |
| |
| // Called on each change of operational state |
| function void on_state_change(operational_state_e new_state); |
| last_err_bits = 0; |
| last_mnem = '0; |
| // Sample bad internal state related signals here because otherwise they tend to not get |
| // captured. |
| bad_internal_state_cg.sample(cfg.trace_vif.urnd_all_zero_q, |
| cfg.trace_vif.insn_addr_err_q, |
| cfg.trace_vif.scramble_state_err_q, |
| cfg.trace_vif.predec_err_q, |
| cfg.trace_vif.missed_gnt_q, |
| cfg.trace_vif.controller_bad_int_q, |
| cfg.trace_vif.start_stop_bad_int_q, |
| cfg.trace_vif.rf_base_spurious_we_err_q, |
| cfg.trace_vif.rf_bignum_spurious_we_err_q, |
| cfg.trace_vif.ext_mubi_err_q); |
| |
| internal_intg_err_cg.sample(cfg.trace_vif.internal_intg_err_q); |
| endfunction |
| |
| function void on_write_to_wr_csr(uvm_reg csr, logic [31:0] data, operational_state_e state); |
| // Ignore writes with the value that's there already |
| if (data == csr.get_mirrored_value()) return; |
| |
| // Otherwise, set an entry in last_write_state |
| last_write_state[csr_str_t'(csr.get_name())] = state; |
| endfunction |
| |
| // Handle coverage for external (bus-accessible) CSRs. |
| // |
| // This runs just before calling predict to update the RAL model, so the old value of the CSR can |
| // be read with csr.get_mirrored_value(). |
| function void on_ext_csr_access(uvm_reg csr, otbn_env_pkg::access_e access_type, |
| logic [31:0] data, operational_state_e state); |
| |
| csr_str_t csr_name = csr_str_t'(csr.get_name()); |
| bit track_write_then_read = 1'b0; |
| |
| case (csr_name) |
| "cmd": begin |
| ext_csr_cmd_cg.sample(otbn_pkg::cmd_e'(data), access_type, state); |
| end |
| "ctrl": begin |
| track_write_then_read = 1'b1; |
| ext_csr_ctrl_cg.sample(data[0], access_type, state); |
| end |
| "status": begin |
| ext_csr_status_cg.sample(otbn_pkg::status_e'(data), access_type); |
| end |
| "err_bits": begin |
| last_err_bits = data; |
| ext_csr_err_bits_cg.sample(otbn_pkg::err_bits_t'(data), |
| csr.get_mirrored_value(), access_type, state); |
| end |
| "fatal_alert_cause": begin |
| ext_csr_fatal_alert_cause_cg.sample(data, access_type, state); |
| |
| // If we read a FATAL_ALERT_CAUSE of (1 << 7) = FATAL_SOFTWARE and we're in a locked state |
| // then we should sample promoted_err_cg to track what SW error was promoted. |
| if ((state == OperationalStateLocked) && |
| (access_type == AccessSoftwareRead) && |
| (data == (1 << 7))) begin |
| promoted_err_cg.sample(last_err_bits[5:0]); |
| end |
| end |
| "insn_cnt": begin |
| ext_csr_insn_cnt_cg.sample(data, csr.get_mirrored_value(), access_type, state); |
| end |
| "load_checksum": begin |
| track_write_then_read = 1'b1; |
| |
| // Special handling for the "write; update; read" sequence. |
| case (wur_state) |
| WUR_IDLE: begin |
| // The WUR_IDLE -> WUR_WRITTEN_CSR transition should happen if we see a write to the |
| // register that will change its value and we're in an operational state where it will |
| // take effect. |
| if ((data != csr.get_mirrored_value()) && |
| (access_type == AccessSoftwareWrite) && |
| (state == OperationalStateIdle)) begin |
| wur_state = WUR_WRITTEN_CSR; |
| end |
| end |
| WUR_WRITTEN_CSR: begin |
| // We can't leave the WUR_WRITTEN_CSR state by accessing the CSR (see on_mem_write()) |
| end |
| WUR_UPDATED_MEM: begin |
| // If we read the CSR after updating memory, sample from the covergroup and go back to |
| // WUR_IDLE. |
| if (access_type == AccessSoftwareRead) begin |
| ext_csr_load_checksum_wur_cg.sample(); |
| wur_state = WUR_IDLE; |
| end |
| end |
| default: `DV_CHECK_FATAL(0) |
| endcase |
| end |
| default: ; // We only track some registers with functional coverage. |
| endcase |
| |
| // Track "write then read" across operational states |
| if (track_write_then_read) begin |
| if (access_type == AccessSoftwareWrite) begin |
| on_write_to_wr_csr(csr, data, state); |
| end else begin |
| if (last_write_state.exists(csr_name)) begin |
| ext_csr_wr_operational_state_cg.sample(csr_name, last_write_state[csr_name]); |
| end |
| end |
| end |
| endfunction |
| |
| // Handle coverage for bus writes to memory |
| function void on_mem_write(uvm_mem mem, |
| bit [31:0] offset, |
| logic [31:0] data, |
| operational_state_e state); |
| // Handle the "write; update; read" sequence for LOAD_CHECKSUM. If we're in the WUR_WRITTEN_CSR |
| // state and this write will have an effect (because we're in the idle state), step to |
| // WUR_UPDATED_MEM. |
| if ((wur_state == WUR_WRITTEN_CSR) && (state == OperationalStateIdle)) begin |
| wur_state = WUR_UPDATED_MEM; |
| end |
| endfunction |
| |
| function void on_tl_write(uvm_reg_addr_t addr, logic [31:0] data, operational_state_e state); |
| // Track attempted writes to the scratchpad memory |
| if (state == OperationalStateIdle) begin |
| scratchpad_writes_cg.sample(addr); |
| end |
| endfunction |
| |
| // Handle coverage for an instruction that was executed |
| // |
| // Almost all the tracking is done based on rtl_item, which comes from the DUT. Our only use for |
| // iss_item is to extract the instruction mnemonic (to avoid needing it, we'd have to implement a |
| // decoder in the coverage code, which doesn't seem like the right thing to do). |
| // |
| function void on_insn(otbn_model_item iss_item, otbn_trace_item rtl_item); |
| string encoding; |
| |
| mnem_str_t mnem; |
| logic [31:0] insn_data; |
| logic [31:0] loop_bodysize; |
| logic call_stack_push, call_stack_pop, call_stack_overflow, call_stack_underflow; |
| |
| logic signed [31:0] addr; |
| logic [9:0] imm10; |
| logic [11:0] imm12; |
| logic [12:0] imm13; |
| logic [14:0] imm15; |
| logic [20:0] imm21; |
| |
| // Since iss_item and rtl_item have come in separately, we do a quick check here to make sure |
| // they actually match the same instruction. |
| `DV_CHECK_EQ(iss_item.insn_addr, rtl_item.insn_addr) |
| |
| // iss_item.mnemonic is a "string". We have to cast this to an integral type (mnem_str_t) to use |
| // it for bins in a coverpoint. This type is chosen to be long enough to hold each valid |
| // mnemonic, but it can't hurt to make absolutely sure that nothing overflows. |
| `DV_CHECK_FATAL(iss_item.mnemonic.len() <= MNEM_STR_LEN) |
| |
| mnem = mnem_str_t'(iss_item.mnemonic); |
| insn_data = rtl_item.insn_data; |
| |
| // Track and catch when we are on the top of the instruction memory and have a straight-line |
| // instruction. |
| insn_addr_cg.sample(mnem, rtl_item.insn_addr); |
| |
| // Call stack tracking. Some instructions want to know whether they are over- or under-flowing |
| // the call stack so, as well as sampling in the call_stack_cg covergroup, we also compute those |
| // flags which we'll use below. |
| call_stack_cg.sample(rtl_item.call_stack_flags, rtl_item.call_stack_fullness); |
| |
| call_stack_push = rtl_item.call_stack_flags.push; |
| call_stack_pop = rtl_item.call_stack_flags.pop_a | rtl_item.call_stack_flags.pop_b; |
| |
| call_stack_overflow = ((rtl_item.call_stack_fullness == StackFull) && |
| call_stack_push && !call_stack_pop); |
| call_stack_underflow = (rtl_item.call_stack_fullness == StackEmpty) && call_stack_pop; |
| |
| // Flag set/clear tracking |
| for (int fg = 0; fg < 2; fg++) begin |
| if (rtl_item.flags_write_valid[fg]) begin |
| flag_write_cg.sample(fg[0], rtl_item.flags_read_data[fg], rtl_item.flags_write_data[fg]); |
| end |
| end |
| |
| // Track pairwise instructions. last_mnem is zero if this is the first instruction since |
| // starting an operation. |
| if (last_mnem != '0) begin |
| pairwise_insn_cg.sample(last_mnem, mnem); |
| end |
| last_mnem = mnem; |
| |
| // Per-encoding coverage. First, use insn_encodings to find the encoding for the instruction. |
| // Every instruction mnemonic should have an associated encoding schema. |
| encoding = insn_encodings[mnem]; |
| case (encoding) |
| "bna": |
| enc_bna_cg.sample(mnem, insn_data, |
| rtl_item.wdr_operand_a, rtl_item.wdr_operand_b, |
| rtl_item.flags_write_data, rtl_item.wdr_write_data); |
| "bnaf": |
| enc_bnaf_cg.sample(mnem, insn_data, |
| rtl_item.wdr_operand_a, rtl_item.wdr_operand_b, |
| rtl_item.flags_write_data); |
| "bnai": |
| enc_bnai_cg.sample(mnem, insn_data, |
| rtl_item.wdr_operand_a, |
| rtl_item.flags_write_data); |
| "bnam": |
| enc_bnam_cg.sample(mnem, insn_data, rtl_item.wdr_operand_a, rtl_item.wdr_operand_b); |
| "bnan": |
| enc_bnan_cg.sample(mnem, insn_data, |
| rtl_item.wdr_operand_a, |
| rtl_item.flags_write_data, |
| rtl_item.wdr_write_data); |
| "bnaq": |
| enc_bnaq_cg.sample(mnem, insn_data, rtl_item.wdr_operand_a, rtl_item.wdr_operand_b); |
| "bnaqs": |
| enc_bnaqs_cg.sample(mnem, insn_data, |
| rtl_item.wdr_operand_a, rtl_item.wdr_operand_b, |
| rtl_item.flags_write_data); |
| "bnaqw": |
| enc_bnaqw_cg.sample(mnem, insn_data, |
| rtl_item.wdr_operand_a, rtl_item.wdr_operand_b, |
| rtl_item.flags_write_data); |
| "bnc": |
| enc_bnc_cg.sample(mnem, insn_data, |
| rtl_item.wdr_operand_a, rtl_item.wdr_operand_b, |
| rtl_item.flags_write_data); |
| "bnmov": |
| enc_bnmov_cg.sample(mnem, insn_data, rtl_item.wdr_operand_a); |
| "bnmovr": |
| enc_bnmovr_cg.sample(mnem, insn_data, rtl_item.gpr_operand_a, rtl_item.gpr_operand_b); |
| "bnr": |
| enc_bnr_cg.sample(mnem, insn_data, rtl_item.wdr_operand_a, rtl_item.wdr_operand_b); |
| "bns": |
| enc_bns_cg.sample(mnem, insn_data, |
| rtl_item.wdr_operand_a, rtl_item.wdr_operand_b, |
| rtl_item.flags_read_data); |
| "bnxid": |
| enc_bnxid_cg.sample(mnem, insn_data, rtl_item.gpr_operand_a, rtl_item.gpr_operand_b); |
| "B": |
| enc_b_cg.sample(mnem, insn_data, rtl_item.gpr_operand_a, rtl_item.gpr_operand_b); |
| "ecall": |
| enc_ecall_cg.sample(mnem, insn_data); |
| "I": |
| enc_i_cg.sample(mnem, insn_data, rtl_item.gpr_operand_a); |
| "Is": |
| enc_is_cg.sample(mnem, insn_data, rtl_item.gpr_operand_a); |
| "J": |
| enc_j_cg.sample(mnem, insn_data); |
| "loop": |
| enc_loop_cg.sample(mnem, insn_data, rtl_item.gpr_operand_a); |
| "loopi": |
| enc_loopi_cg.sample(mnem, insn_data); |
| "R": |
| enc_r_cg.sample(mnem, insn_data, rtl_item.gpr_operand_a, rtl_item.gpr_operand_b); |
| "S": |
| enc_s_cg.sample(mnem, insn_data, rtl_item.gpr_operand_a, rtl_item.gpr_operand_b); |
| "U": |
| enc_u_cg.sample(mnem, insn_data); |
| "wcsr": |
| enc_wcsr_cg.sample(mnem, insn_data); |
| "dummy": |
| // Bad instruction: no encoding-level coverage. |
| ; |
| default: `dv_fatal($sformatf("Unknown encoding (%0s) for instruction `%0s'", encoding, mnem), |
| `gfn) |
| endcase |
| |
| // LOOP and LOOPI instructions store bodysize as an immediate in bits 31:20, but shifted by 1 |
| // (so a bodysize of 1 is encoded as 0 etc.) |
| loop_bodysize = 32'(insn_data[31:20]) + 32'd1; |
| |
| // Instruction-specific coverage. |
| case (mnem) |
| mnem_add, mnem_sub: |
| insn_addsub_cg.sample(mnem, rtl_item.gpr_operand_a, rtl_item.gpr_operand_b); |
| mnem_addi: |
| insn_addi_cg.sample(insn_data, rtl_item.gpr_operand_a); |
| mnem_sll: |
| insn_sll_cg.sample(rtl_item.gpr_operand_a, rtl_item.gpr_operand_b); |
| mnem_slli: |
| insn_slli_cg.sample(insn_data, rtl_item.gpr_operand_a); |
| mnem_srl: |
| insn_srl_cg.sample(rtl_item.gpr_operand_a, rtl_item.gpr_operand_b); |
| mnem_srli: |
| insn_srli_cg.sample(insn_data, rtl_item.gpr_operand_a); |
| mnem_sra: |
| insn_sra_cg.sample(rtl_item.gpr_operand_a, rtl_item.gpr_operand_b); |
| mnem_srai: |
| insn_srai_cg.sample(insn_data, rtl_item.gpr_operand_a); |
| mnem_and, mnem_andi, mnem_or, mnem_ori, mnem_xor, mnem_xori: |
| // This covergroup tracks write data, so we only sample when GRD is nonzero |
| if (insn_data[11:7] != 0) begin |
| insn_log_binop_cg.sample(mnem, insn_data, rtl_item.gpr_write_data); |
| end |
| mnem_lw: begin |
| imm12 = insn_data[31:20]; |
| addr = $signed(rtl_item.gpr_operand_a) + $signed(imm12); |
| insn_xw_cg.sample(mnem, |
| imm12, |
| insn_data[19:15] /* grs1 */, |
| insn_data[11:7] /* grd */, |
| rtl_item.gpr_operand_a, |
| addr, |
| rtl_item.call_stack_fullness, |
| call_stack_underflow); |
| end |
| mnem_sw: begin |
| imm12 = {insn_data[31:25], insn_data[11:7]}; |
| addr = $signed(rtl_item.gpr_operand_a) + $signed(imm12); |
| insn_xw_cg.sample(mnem, |
| imm12, |
| insn_data[19:15] /* grs1 */, |
| insn_data[24:20] /* grs2 */, |
| rtl_item.gpr_operand_a, |
| addr, |
| rtl_item.call_stack_fullness, |
| call_stack_underflow); |
| end |
| mnem_beq, mnem_bne: begin |
| imm13 = {insn_data[31], insn_data[7], insn_data[30:25], insn_data[11:8], 1'b0}; |
| addr = $signed(rtl_item.insn_addr) + $signed(imm13); |
| insn_bxx_cg.sample(mnem, |
| rtl_item.insn_addr, |
| imm13, |
| rtl_item.gpr_operand_a, |
| rtl_item.gpr_operand_b, |
| addr, |
| rtl_item.at_current_loop_end_insn, |
| call_stack_underflow); |
| end |
| mnem_jal: begin |
| imm21 = {insn_data[31], insn_data[19:12], insn_data[20], insn_data[30:21], 1'b0}; |
| addr = $signed(rtl_item.insn_addr) + $signed(imm21); |
| insn_jal_cg.sample(rtl_item.insn_addr, |
| imm21, |
| addr, |
| rtl_item.at_current_loop_end_insn, |
| call_stack_overflow); |
| end |
| mnem_jalr: begin |
| imm12 = insn_data[31:20]; |
| addr = $signed(rtl_item.gpr_operand_a) + $signed(imm12); |
| insn_jalr_cg.sample(rtl_item.insn_addr, |
| imm12, |
| addr, |
| rtl_item.gpr_operand_a, |
| rtl_item.at_current_loop_end_insn, |
| call_stack_underflow, |
| call_stack_overflow); |
| end |
| mnem_csrrs: |
| insn_csrrs_cg.sample(insn_data, |
| rtl_item.gpr_operand_a, |
| rtl_item.call_stack_fullness); |
| mnem_csrrw: |
| insn_csrrw_cg.sample(insn_data, |
| rtl_item.call_stack_fullness); |
| mnem_loop: |
| insn_loop_cg.sample(rtl_item.insn_addr, |
| insn_data, |
| rtl_item.gpr_operand_a, |
| rtl_item.loop_stack_fullness, |
| rtl_item.current_loop_end, |
| loop_bodysize, |
| rtl_item.at_current_loop_end_insn, |
| call_stack_underflow); |
| mnem_loopi: begin |
| imm10 = {insn_data[19:15], insn_data[11:7]}; |
| insn_loopi_cg.sample(rtl_item.insn_addr, |
| insn_data, |
| rtl_item.loop_stack_fullness, |
| rtl_item.current_loop_end, |
| imm10, |
| loop_bodysize, |
| rtl_item.at_current_loop_end_insn); |
| end |
| mnem_bn_addc: |
| insn_bn_addc_cg.sample(insn_data, |
| rtl_item.flags_read_data); |
| mnem_bn_addm: |
| insn_bn_addm_cg.sample(insn_data, |
| rtl_item.wdr_operand_a, |
| rtl_item.wdr_operand_b, |
| rtl_item.mod); |
| mnem_bn_mulqacc, mnem_bn_mulqacc_so, mnem_bn_mulqacc_wo: |
| insn_bn_mulqaccx_cg.sample(mnem, |
| rtl_item.new_acc_extended); |
| mnem_bn_subb, mnem_bn_cmpb: |
| insn_bn_subcmpb_cg.sample(mnem, |
| insn_data, |
| rtl_item.flags_read_data); |
| mnem_bn_subm: |
| insn_bn_subm_cg.sample(insn_data, |
| rtl_item.wdr_operand_a, |
| rtl_item.wdr_operand_b, |
| rtl_item.mod); |
| mnem_bn_lid, mnem_bn_sid: begin |
| logic inc_both = insn_data[8] && insn_data[7]; |
| logic [4:0] grs1 = insn_data[19:15]; |
| logic [4:0] grx2 = insn_data[24:20]; // Either GRD or GRS2 |
| logic local_x1_uflow; |
| |
| imm15 = {insn_data[11:9], insn_data[31:25], 5'b0}; |
| addr = $signed(rtl_item.gpr_operand_a) + $signed(imm15); |
| |
| // Compute our own definition of call_stack_underflow. This should match the existing one if |
| // !inc_both. However, when both increments are set the RTL decoder squashes the call stack |
| // update flags so we have to figure them out ourselves. |
| local_x1_uflow = (rtl_item.call_stack_fullness == StackEmpty) && |
| ((grs1 == 5'd1) || (grx2 == 5'd1)); |
| `DV_CHECK_FATAL(local_x1_uflow || !call_stack_underflow) |
| `DV_CHECK_FATAL(inc_both || call_stack_underflow || !local_x1_uflow) |
| |
| insn_bn_xid_cg.sample(mnem, |
| imm15, |
| rtl_item.gpr_operand_a, |
| rtl_item.gpr_operand_b, |
| addr, |
| inc_both, |
| grs1, |
| grx2, |
| local_x1_uflow); |
| end |
| mnem_bn_movr: begin |
| logic inc_both = insn_data[9] && insn_data[7]; |
| logic [4:0] grs = insn_data[19:15]; |
| logic [4:0] grd = insn_data[24:20]; |
| logic local_x1_uflow; |
| |
| // Compute our own definition of call_stack_underflow. This should match the existing one if |
| // !inc_both. However, when both increments are set the RTL decoder squashes the call stack |
| // update flags so we have to figure them out ourselves. |
| local_x1_uflow = (rtl_item.call_stack_fullness == StackEmpty) && |
| ((grs == 5'd1) || (grd == 5'd1)); |
| `DV_CHECK_FATAL(local_x1_uflow || !call_stack_underflow) |
| `DV_CHECK_FATAL(inc_both || call_stack_underflow || !local_x1_uflow) |
| |
| insn_bn_movr_cg.sample(rtl_item.gpr_operand_a, |
| rtl_item.gpr_operand_b, |
| inc_both, |
| grs, |
| grd, |
| local_x1_uflow); |
| end |
| mnem_bn_wsrr: begin |
| logic [7:0] wsr_imm = insn_data[27:20]; |
| insn_bn_wsrr_cg.sample(wsr_imm, rtl_item.has_sideload_key); |
| end |
| default: |
| // No special handling for this instruction yet. |
| ; |
| endcase |
| endfunction |
| |
| `undef DEF_MNEM_BIN |
| `undef DEF_MNEM_BINS_EXCEPT_ECALL |
| `undef DEF_MNEM_CROSS |
| `undef DEF_MNEM_CROSS2 |
| `undef DEF_MNEM_CROSS3 |
| `undef GPR_BIN_TYPES |
| `undef DEF_GPR_CP |
| `undef _DEF_TOGGLE_COV_1 |
| `undef _DEF_TOGGLE_COV_2 |
| `undef _DEF_TOGGLE_COV_4 |
| `undef _DEF_TOGGLE_COV_8 |
| `undef _DEF_TOGGLE_COV_16 |
| `undef _DEF_TOGGLE_COV_32 |
| `undef _DEF_TOGGLE_COV_64 |
| `undef _DEF_TOGGLE_COV_128 |
| `undef DEF_GPR_TOGGLE_COV |
| `undef DEF_WDR_TOGGLE_COV |
| `undef _DEF_TOGGLE_CROSS_1 |
| `undef _DEF_TOGGLE_CROSS_2 |
| `undef _DEF_TOGGLE_CROSS_4 |
| `undef _DEF_TOGGLE_CROSS_8 |
| `undef _DEF_TOGGLE_CROSS_16 |
| `undef _DEF_TOGGLE_CROSS_32 |
| `undef _DEF_TOGGLE_CROSS_64 |
| `undef _DEF_TOGGLE_CROSS_128 |
| `undef DEF_GPR_TOGGLE_CROSS |
| `undef DEF_WDR_TOGGLE_CROSS |
| `undef DEF_SIGN_CP |
| `undef _NZ_CP_BINS |
| `undef DEF_NZ_CP |
| `undef DEF_NZ_IF_CP |
| `undef DEF_SEEN_CP |
| `undef DEF_SEEN_IF_CP |
| `undef DEF_CSR_CP |
| |
| endclass |