diff --git a/doc/01_overview/compliance.rst b/doc/01_overview/compliance.rst index bdb039cd82..f9436be88d 100644 --- a/doc/01_overview/compliance.rst +++ b/doc/01_overview/compliance.rst @@ -47,6 +47,14 @@ In addition, the following instruction set extensions are available. - 2.0 - always enabled + * - **Zcb**: Simple Code-Size Saving Instructions + - 1.0.0 + - optional + + * - **Zcmp**: Push/Pop/Move Code-Size Saving Instructions + - 1.0.0 + - optional + * - **Smepmp** - PMP Enhancements for memory access and execution prevention on Machine mode - 1.0 - always enabled in configurations with PMP see :ref:`PMP Enhancements` diff --git a/doc/02_user/integration.rst b/doc/02_user/integration.rst index c002de571f..6d835c9ce3 100644 --- a/doc/02_user/integration.rst +++ b/doc/02_user/integration.rst @@ -99,6 +99,7 @@ Instantiation Template .RV32E ( 0 ), .RV32M ( ibex_pkg::RV32MFast ), .RV32B ( ibex_pkg::RV32BNone ), + .RV32ZC ( ibex_pkg::RV32ZcaZcbZcmp ), .RegFile ( ibex_pkg::RegFileFF ), .ICache ( 0 ), .ICacheECC ( 0 ), @@ -168,72 +169,78 @@ Instantiation Template Parameters ---------- -+------------------------------+---------------------+------------+-----------------------------------------------------------------------+ -| Name | Type/Range | Default | Description | -+==============================+=====================+============+=======================================================================+ -| ``PMPEnable`` | bit | 0 | Enable PMP support | -+------------------------------+---------------------+------------+-----------------------------------------------------------------------+ -| ``PMPGranularity`` | int (0..31) | 0 | Minimum granularity of PMP address matching | -+------------------------------+---------------------+------------+-----------------------------------------------------------------------+ -| ``PMPNumRegions`` | int (1..16) | 4 | Number implemented PMP regions (ignored if PMPEnable == 0) | -+------------------------------+---------------------+------------+-----------------------------------------------------------------------+ -| ``MHPMCounterNum`` | int (0..10) | 0 | Number of performance monitor event counters | -+------------------------------+---------------------+------------+-----------------------------------------------------------------------+ -| ``MHPMCounterWidth`` | int (64..1) | 40 | Bit width of performance monitor event counters | -+------------------------------+---------------------+------------+-----------------------------------------------------------------------+ -| ``RV32E`` | bit | 0 | RV32E mode enable (16 integer registers only) | -+------------------------------+---------------------+------------+-----------------------------------------------------------------------+ -| ``RV32M`` | ibex_pkg::rv32m_e | RV32MFast | M(ultiply) extension select: | -| | | | "ibex_pkg::RV32MNone": No M-extension | -| | | | "ibex_pkg::RV32MSlow": Slow multi-cycle multiplier, iterative divider | -| | | | "ibex_pkg::RV32MFast": 3-4 cycle multiplier, iterative divider | -| | | | "ibex_pkg::RV32MSingleCycle": 1-2 cycle multiplier, iterative divider | -+------------------------------+---------------------+------------+-----------------------------------------------------------------------+ -| ``RV32B`` | ibex_pkg::rv32b_e | RV32BNone | B(itmanipulation) extension select: | -| | | | "ibex_pkg::RV32BNone": No B-extension | -| | | | "ibex_pkg::RV32BBalanced": Sub-extensions Zba, Zbb, Zbs, Zbf and Zbt | -| | | | "ibex_pkg::RV32BOTEarlGrey": All sub-extensions except Zbe | -| | | | "ibex_pkg::RV32BFull": All sub-extensions | -+------------------------------+---------------------+------------+-----------------------------------------------------------------------+ -| ``RegFile`` | ibex_pkg::regfile_e | RegFileFF | Register file implementation select: | -| | | | "ibex_pkg::RegFileFF": Generic flip-flop-based register file | -| | | | "ibex_pkg::RegFileFPGA": Register file for FPGA targets | -| | | | "ibex_pkg::RegFileLatch": Latch-based register file for ASIC targets | -+------------------------------+---------------------+------------+-----------------------------------------------------------------------+ -| ``BranchTargetALU`` | bit | 0 | Enables branch target ALU removing a stall cycle from taken branches | -+------------------------------+---------------------+------------+-----------------------------------------------------------------------+ -| ``WritebackStage`` | bit | 0 | Enables third pipeline stage (writeback) improving performance of | -| | | | loads and stores | -+------------------------------+---------------------+------------+-----------------------------------------------------------------------+ -| ``ICache`` | bit | 0 | Enable instruction cache instead of prefetch buffer | -+------------------------------+---------------------+------------+-----------------------------------------------------------------------+ -| ``ICacheECC`` | bit | 0 | Enable SECDED ECC protection in ICache (if ICache == 1) | -+------------------------------+---------------------+------------+-----------------------------------------------------------------------+ -| ``ICacheScramble`` | bit | 0 | Enabling this parameter replaces tag and data RAMs of ICache with | -| | | | scrambling RAM primitives. | -+------------------------------+---------------------+------------+-----------------------------------------------------------------------+ -| ``BranchPrediction`` | bit | 0 | *EXPERIMENTAL* Enable Static branch prediction | -+------------------------------+---------------------+------------+-----------------------------------------------------------------------+ -| ``SecureIbex`` | bit | 0 | Enable various additional features targeting secure code execution. | -| | | | Note: SecureIbex == 1'b1 and RV32M == ibex_pkg::RV32MNone is an | -| | | | illegal combination. | -+------------------------------+---------------------+------------+-----------------------------------------------------------------------+ -| ``RndCnstLfsrSeed`` | lfsr_seed_t | see above | Set the starting seed of the LFSR used to generate dummy instructions | -| | | | (only relevant when SecureIbex == 1'b1) | -+------------------------------+---------------------+------------+-----------------------------------------------------------------------+ -| ``RndCnstLfsrPerm`` | lfsr_perm_t | see above | Set the permutation applied to the output of the LFSR used to | -| | | | generate dummy instructions (only relevant when SecureIbex == 1'b1) | -+------------------------------+---------------------+------------+-----------------------------------------------------------------------+ -| ``DbgTriggerEn`` | bit | 0 | Enable debug trigger support (one trigger only) | -+------------------------------+---------------------+------------+-----------------------------------------------------------------------+ -| ``DmBaseAddr`` | int | 0x1A110000 | Base address of the Debug Module | -+------------------------------+---------------------+------------+-----------------------------------------------------------------------+ -| ``DmAddrMask`` | int | 0x1A110000 | Address mask of the Debug Module | -+------------------------------+---------------------+------------+-----------------------------------------------------------------------+ -| ``DmHaltAddr`` | int | 0x1A110800 | Address to jump to when entering Debug Mode | -+------------------------------+---------------------+------------+-----------------------------------------------------------------------+ -| ``DmExceptionAddr`` | int | 0x1A110808 | Address to jump to when an exception occurs while in Debug Mode | -+------------------------------+---------------------+------------+-----------------------------------------------------------------------+ ++------------------------------+---------------------+----------------+-----------------------------------------------------------------------+ +| Name | Type/Range | Default | Description | ++==============================+=====================+================+=======================================================================+ +| ``PMPEnable`` | bit | 0 | Enable PMP support | ++------------------------------+---------------------+----------------+-----------------------------------------------------------------------+ +| ``PMPGranularity`` | int (0..31) | 0 | Minimum granularity of PMP address matching | ++------------------------------+---------------------+----------------+-----------------------------------------------------------------------+ +| ``PMPNumRegions`` | int (1..16) | 4 | Number implemented PMP regions (ignored if PMPEnable == 0) | ++------------------------------+---------------------+----------------+-----------------------------------------------------------------------+ +| ``MHPMCounterNum`` | int (0..10) | 0 | Number of performance monitor event counters | ++------------------------------+---------------------+----------------+-----------------------------------------------------------------------+ +| ``MHPMCounterWidth`` | int (64..1) | 40 | Bit width of performance monitor event counters | ++------------------------------+---------------------+----------------+-----------------------------------------------------------------------+ +| ``RV32E`` | bit | 0 | RV32E mode enable (16 integer registers only) | ++------------------------------+---------------------+----------------+-----------------------------------------------------------------------+ +| ``RV32M`` | ibex_pkg::rv32m_e | RV32MFast | M(ultiply) extension select: | +| | | | "ibex_pkg::RV32MNone": No M-extension | +| | | | "ibex_pkg::RV32MSlow": Slow multi-cycle multiplier, iterative divider | +| | | | "ibex_pkg::RV32MFast": 3-4 cycle multiplier, iterative divider | +| | | | "ibex_pkg::RV32MSingleCycle": 1-2 cycle multiplier, iterative divider | ++------------------------------+---------------------+----------------+-----------------------------------------------------------------------+ +| ``RV32B`` | ibex_pkg::rv32b_e | RV32BNone | B(itmanipulation) extension select: | +| | | | "ibex_pkg::RV32BNone": No B-extension | +| | | | "ibex_pkg::RV32BBalanced": Sub-extensions Zba, Zbb, Zbs, Zbf and Zbt | +| | | | "ibex_pkg::RV32BOTEarlGrey": All sub-extensions except Zbe | +| | | | "ibex_pkg::RV32BFull": All sub-extensions | ++------------------------------+---------------------+----------------+-----------------------------------------------------------------------+ +| ``RV32ZC`` | ibex_pkg::rv32zc_e | RV32ZcaZcbZcmp | Zc code-size saving extension select: | +| | | | "ibex_pkg::RV32Zca": The Zca extension | +| | | | "ibex_pkg::RV32ZcaZcb": Zca and Zcb extensions | +| | | | "ibex_pkg::RV32ZcaZcmp": Zca and Zcmp extensions | +| | | | "ibex_pkg::RV32ZcaZcbZcmp": Zca, Zcb, and Zcmp extensions | ++------------------------------+---------------------+----------------+-----------------------------------------------------------------------+ +| ``RegFile`` | ibex_pkg::regfile_e | RegFileFF | Register file implementation select: | +| | | | "ibex_pkg::RegFileFF": Generic flip-flop-based register file | +| | | | "ibex_pkg::RegFileFPGA": Register file for FPGA targets | +| | | | "ibex_pkg::RegFileLatch": Latch-based register file for ASIC targets | ++------------------------------+---------------------+----------------+-----------------------------------------------------------------------+ +| ``BranchTargetALU`` | bit | 0 | Enables branch target ALU removing a stall cycle from taken branches | ++------------------------------+---------------------+----------------+-----------------------------------------------------------------------+ +| ``WritebackStage`` | bit | 0 | Enables third pipeline stage (writeback) improving performance of | +| | | | loads and stores | ++------------------------------+---------------------+----------------+-----------------------------------------------------------------------+ +| ``ICache`` | bit | 0 | Enable instruction cache instead of prefetch buffer | ++------------------------------+---------------------+----------------+-----------------------------------------------------------------------+ +| ``ICacheECC`` | bit | 0 | Enable SECDED ECC protection in ICache (if ICache == 1) | ++------------------------------+---------------------+----------------+-----------------------------------------------------------------------+ +| ``ICacheScramble`` | bit | 0 | Enabling this parameter replaces tag and data RAMs of ICache with | +| | | | scrambling RAM primitives. | ++------------------------------+---------------------+----------------+-----------------------------------------------------------------------+ +| ``BranchPrediction`` | bit | 0 | *EXPERIMENTAL* Enable Static branch prediction | ++------------------------------+---------------------+----------------+-----------------------------------------------------------------------+ +| ``SecureIbex`` | bit | 0 | Enable various additional features targeting secure code execution. | +| | | | Note: SecureIbex == 1'b1 and RV32M == ibex_pkg::RV32MNone is an | +| | | | illegal combination. | ++------------------------------+---------------------+----------------+-----------------------------------------------------------------------+ +| ``RndCnstLfsrSeed`` | lfsr_seed_t | see above | Set the starting seed of the LFSR used to generate dummy instructions | +| | | | (only relevant when SecureIbex == 1'b1) | ++------------------------------+---------------------+----------------+-----------------------------------------------------------------------+ +| ``RndCnstLfsrPerm`` | lfsr_perm_t | see above | Set the permutation applied to the output of the LFSR used to | +| | | | generate dummy instructions (only relevant when SecureIbex == 1'b1) | ++------------------------------+---------------------+----------------+-----------------------------------------------------------------------+ +| ``DbgTriggerEn`` | bit | 0 | Enable debug trigger support (one trigger only) | ++------------------------------+---------------------+----------------+-----------------------------------------------------------------------+ +| ``DmBaseAddr`` | int | 0x1A110000 | Base address of the Debug Module | ++------------------------------+---------------------+----------------+-----------------------------------------------------------------------+ +| ``DmAddrMask`` | int | 0x1A110000 | Address mask of the Debug Module | ++------------------------------+---------------------+----------------+-----------------------------------------------------------------------+ +| ``DmHaltAddr`` | int | 0x1A110800 | Address to jump to when entering Debug Mode | ++------------------------------+---------------------+----------------+-----------------------------------------------------------------------+ +| ``DmExceptionAddr`` | int | 0x1A110808 | Address to jump to when an exception occurs while in Debug Mode | ++------------------------------+---------------------+----------------+-----------------------------------------------------------------------+ Any parameter marked *EXPERIMENTAL* when enabled is not verified to the same standard as the rest of the Ibex core. diff --git a/doc/03_reference/pipeline_details.rst b/doc/03_reference/pipeline_details.rst index e0d663430d..384b359412 100644 --- a/doc/03_reference/pipeline_details.rst +++ b/doc/03_reference/pipeline_details.rst @@ -93,3 +93,13 @@ Read the description for more information. | | | jump (which does the required flushing) so it has the same | | | | stall characteristics (see above). | +-----------------------+--------------------------------------+-------------------------------------------------------------+ +| Zcmp Push/Pop | 2 - N | The cm.push/pop instructions as defined in 'Zcmp' of the | +| | | RISC-V specification. Internally, they expand to multiple | +| | | register load/store operations combined with stack pointer | +| | | adjustments, so their latency corresponds to the total | +| | | number of instructions issued and the memory latency. | ++-----------------------+--------------------------------------+-------------------------------------------------------------+ +| Zcmp Move | 2 | The `cm.mvsa01` and `cm.mva01s` instruction as defined in | +| | | 'Zcmp' of the RISC-V specification. Internally, they are | +| | | implemented as two `addi rd, rs1, 0` instructions. | ++-----------------------+--------------------------------------+-------------------------------------------------------------+ diff --git a/dv/formal/check/top.sv b/dv/formal/check/top.sv index a301af0862..a10d7a4f8f 100644 --- a/dv/formal/check/top.sv +++ b/dv/formal/check/top.sv @@ -40,6 +40,7 @@ module top import ibex_pkg::*; #( parameter bit SecureIbex = 1'b0, parameter bit WritebackStage = 1'b1, parameter bit RV32E = 1'b0, + parameter rv32zc_e RV32ZC = RV32Zca, parameter int unsigned PMPNumRegions = 4 ) ( // Clock and Reset @@ -131,6 +132,7 @@ ibex_top #( .SecureIbex(SecureIbex), .WritebackStage(WritebackStage), .RV32E(RV32E), + .RV32ZC(RV32ZC), .BranchTargetALU(1'b1), .PMPEnable(1'b1), .PMPNumRegions(PMPNumRegions), @@ -441,25 +443,35 @@ assign ex_is_checkable_csr = ~( logic [31:0] decompressed_instr; logic decompressed_instr_illegal; -ibex_compressed_decoder decompression_assertion_decoder( +ibex_compressed_decoder #( + .RV32ZC(RV32ZC), + .ResetAll(SecureIbex) +) decompression_assertion_decoder ( .clk_i, .rst_ni, .valid_i(1'b1), + .id_in_ready_i(1'b1), .instr_i(ex_compressed_instr), .instr_o(decompressed_instr), .is_compressed_o(), + .gets_expanded_o(), .illegal_instr_o(decompressed_instr_illegal) ); logic [31:0] decompressed_instr_2; logic decompressed_instr_illegal_2; -ibex_compressed_decoder decompression_assertion_decoder_2( +ibex_compressed_decoder #( + .RV32ZC(RV32ZC), + .ResetAll(SecureIbex) +) decompression_assertion_decoder_2( .clk_i, .rst_ni, .valid_i(1'b1), + .id_in_ready_i(1'b1), .instr_i(wbexc_instr), .instr_o(decompressed_instr_2), .is_compressed_o(wbexc_is_compressed), + .gets_expanded_o(), .illegal_instr_o(decompressed_instr_illegal_2) ); diff --git a/rtl/ibex_compressed_decoder.sv b/rtl/ibex_compressed_decoder.sv index e3c6aa52c7..0adf3f0f0d 100644 --- a/rtl/ibex_compressed_decoder.sv +++ b/rtl/ibex_compressed_decoder.sv @@ -13,30 +13,204 @@ `include "prim_assert.sv" -module ibex_compressed_decoder ( - input logic clk_i, - input logic rst_ni, - input logic valid_i, - input logic [31:0] instr_i, - output logic [31:0] instr_o, - output logic is_compressed_o, - output logic illegal_instr_o +module ibex_compressed_decoder #( + parameter ibex_pkg::rv32zc_e RV32ZC = ibex_pkg::RV32ZcaZcbZcmp, + parameter bit ResetAll = 1'b0 +) ( + input logic clk_i, + input logic rst_ni, + input logic valid_i, + input logic id_in_ready_i, + input logic [31:0] instr_i, + output logic [31:0] instr_o, + output logic is_compressed_o, + output ibex_pkg::instr_exp_e gets_expanded_o, + output logic illegal_instr_o ); import ibex_pkg::*; - // valid_i indicates if instr_i is valid and is used for assertions only. - // The following signal is used to avoid possible lint errors. - logic unused_valid; - assign unused_valid = valid_i; + if (!(RV32ZC == RV32ZcaZcbZcmp || RV32ZC == RV32ZcaZcmp)) begin : gen_unused_valid + // valid_i indicates if instr_i is valid and is used for assertions only if Zcmp is disabled. + // id_in_ready_i indicates if instr_o is consumed and is used for assertions only if Zcmp is + // disabled. The following signals are used to avoid possible lint errors. + logic unused_valid; + logic unused_id_in_ready; + assign unused_valid = valid_i; + assign unused_id_in_ready = id_in_ready_i; + end + + function automatic logic [6:0] cm_stack_adj_base(input logic [3:0] rlist); + unique case (rlist) + // Deliberately not written as `case .. inside` because that is not supported by all tools. + 4'd4, 4'd5, 4'd6, 4'd7: return 7'd16; + 4'd8, 4'd9, 4'd10, 4'd11: return 7'd32; + 4'd12, 4'd13, 4'd14: return 7'd48; + 4'd15: return 7'd64; + default: return 7'd0; // illegal + endcase + endfunction + + function automatic logic [6:0] cm_stack_adj(input logic [3:0] rlist, input logic [1:0] spimm); + return cm_stack_adj_base(rlist) + spimm * 16; + endfunction + + function automatic logic [4:0] cm_stack_adj_word(input logic [3:0] rlist, + input logic [1:0] spimm); + logic [6:0] tmp; + logic [1:0] _unused; + tmp = cm_stack_adj(.rlist(rlist), .spimm(spimm)); + _unused = tmp[1:0]; + return tmp[6:2]; + endfunction + + function automatic logic [4:0] cm_rlist_top_reg(input logic [4:0] rlist); + unique case (rlist) + // Deliberately not written as `case .. inside` because that is not supported by all tools. + 5'd16, // `rlist` can be 16 after instruction decoding, to handle `x26`+`x27`. + 5'd15, 5'd14, 5'd13, + 5'd12, 5'd11, 5'd10, + 5'd9, 5'd8, 5'd7: return 5'd11 + rlist; + 5'd6, 5'd5: return 5'd3 + rlist; + 5'd4: return 5'd1; + default: return 5'd0; // illegal + endcase + endfunction + + function automatic logic [31:0] cm_push_store_reg(input logic [4:0] rlist, + input logic [4:0] sp_offset); + logic [11:0] neg_offset; + logic [31:0] instr; + neg_offset = ~{5'b00000, sp_offset, 2'b00} + 12'd1; + instr[ 6: 0] /* opcode */ = OPCODE_STORE; + instr[11: 7] /* offset[4:0] */ = neg_offset[4:0]; + instr[14:12] /* width */ = 3'b010; // 32 bit + instr[19:15] /* base reg */ = 5'd2; // x2 (sp / stack pointer) + instr[24:20] /* src reg */ = cm_rlist_top_reg(rlist); + instr[31:25] /* offset[11:5] */ = neg_offset[11:5]; + return instr; + endfunction + + function automatic logic [31:0] cm_pop_load_reg(input logic [4:0] rlist, + input logic [4:0] sp_offset); + logic [31:0] instr; + instr[ 6: 0] /* opcode */ = OPCODE_LOAD; + instr[11: 7] /* dest reg */ = cm_rlist_top_reg(rlist); + instr[14:12] /* width */ = 3'b010; // 32 bit + instr[19:15] /* base reg */ = 5'd2; // x2 (sp / stack pointer) + instr[31:20] /* offset[11:0] */ = {5'b00000, sp_offset, 2'b00}; + return instr; + endfunction + + function automatic logic [31:0] cm_sp_addi(input logic [3:0] rlist, + input logic [1:0] spimm, + input logic decr = 1'b0); + logic [11:0] imm; + logic [31:0] instr; + imm[11:7] = '0; + imm[ 6:0] = cm_stack_adj(.rlist(rlist), .spimm(spimm)); + if (decr) imm = ~imm + 12'd1; + instr[ 6: 0] /* opcode */ = OPCODE_OP_IMM; + instr[11: 7] /* dest reg */ = 5'd2; // x2 (sp / stack pointer) + instr[14:12] /* funct3 */ = 3'b000; // addi + instr[19:15] /* src reg */ = 5'd2; // x2 + instr[31:20] /* imm[11:0] */ = imm; + return instr; + endfunction + + function automatic logic [31:0] cm_mv_reg(input logic [4:0] src, input logic [4:0] dst); + logic [31:0] instr; + instr[ 6: 0] /* opcode */ = OPCODE_OP_IMM; + instr[11: 7] /* dest reg */ = dst; + instr[14:12] /* funct3 */ = 3'b000; // addi + instr[19:15] /* src reg */ = src; + instr[31:20] /* imm[11:0] */ = 12'd0; // 0 + return instr; + endfunction + + function automatic logic [31:0] cm_zero_a0(); + return cm_mv_reg(.src(5'd0 /* x0 */), .dst(5'd10 /* a0 */)); + endfunction + + function automatic logic [31:0] cm_ret_ra(); + logic [31:0] instr; + instr[ 6: 0] /* opcode */ = OPCODE_JALR; + instr[11: 7] /* dest reg */ = 5'd0; // x0 + instr[14:12] /* funct3 */ = 3'b000; // jalr + instr[19:15] /* base reg */ = 5'd1; // x1 (ra) + instr[31:20] /* offset[11:0] */ = 12'd0; // 0 + return instr; + endfunction + + function automatic logic [31:0] cm_mvsa01(input logic a01, input logic [2:0] rs); + logic [4:0] src, dst; + src = 5'd10 + {4'd0, a01}; + dst = {(rs[2:1] > 2'd0), (rs[2:1] == 2'd0), rs[2:0]}; + return cm_mv_reg(.src(src), .dst(dst)); + endfunction + + function automatic logic [31:0] cm_mva01s(input logic [2:0] rs, input logic a01); + logic [4:0] src, dst; + src = {(rs[2:1] > 2'd0), (rs[2:1] == 2'd0), rs[2:0]}; + dst = 5'd10 + {4'd0, a01}; + return cm_mv_reg(.src(src), .dst(dst)); + endfunction + + function automatic logic [4:0] cm_rlist_init(input logic [3:0] instr_rlist); + logic [4:0] rlist; + rlist = {1'b0, instr_rlist}; + if (rlist == 5'd15) begin + // An `rlist` value of 15 means that x26 and x27 have to be stored. + // Handle this by initializing `rlist` internally to 16. + rlist = 5'd16; + end + return rlist; + endfunction + + // Combined FSM state register for Zcmp operations. + // This single 3-bit enum represents 3 independent FSMs that share the CmIdle state and the + // resources. + typedef enum logic [2:0] { + CmIdle, + // cm.push + CmPushStoreReg, + CmPushDecrSp, + // cm.pop, cm.popret, cm.popretz + CmPopLoadReg, + CmPopIncrSp, + CmPopZeroA0, + CmPopRetRa, + // cm.mvsa01, cm.mva01s + CmMvSecondReg + } cm_state_e; + logic [4:0] cm_rlist_d, cm_rlist_q; + logic [4:0] cm_sp_offset_d, cm_sp_offset_q; + cm_state_e cm_state_d, cm_state_q; + + // Gate the `gets_expanded_o` to ensure that a invalid instruction looking like expandable cm.* + // instructions will not be stalled/blocked in later control logic because it is waiting for + // `INSTR_EXPANDED_LAST`. + ibex_pkg::instr_exp_e gets_expanded; + if (RV32ZC == RV32ZcaZcbZcmp || RV32ZC == RV32ZcaZcmp) begin : gen_gets_expanded + assign gets_expanded_o = valid_i ? gets_expanded : INSTR_NOT_EXPANDED; + end else begin : gen_gets_expanded + // `gets_expanded` will be tied to INSTR_NOT_EXPANDED in this case + assign gets_expanded_o = gets_expanded; + end //////////////////////// // Compressed decoder // //////////////////////// always_comb begin - // By default, forward incoming instruction, mark it as legal. + // By default, forward incoming instruction, mark it as legal, and don't expand. instr_o = instr_i; illegal_instr_o = 1'b0; + gets_expanded = INSTR_NOT_EXPANDED; + + // Maintain state of CM FSM. + cm_rlist_d = cm_rlist_q; + cm_sp_offset_d = cm_sp_offset_q; + cm_state_d = cm_state_q; // Check if incoming instruction is compressed. unique case (instr_i[1:0]) @@ -63,9 +237,70 @@ module ibex_compressed_decoder ( 2'b00, {OPCODE_STORE}}; end + 3'b100: begin // loads and stores + if (RV32ZC == RV32ZcaZcbZcmp || RV32ZC == RV32ZcaZcb) begin + unique case (instr_i[12:10]) + 3'b000: begin + // c.lbu -> lbu rd', imm(rs1') + instr_o = {10'b0, instr_i[5], instr_i[6], 2'b01, instr_i[9:7], + 3'b100, 2'b01, instr_i[4:2], {OPCODE_LOAD}}; + end + + 3'b001: begin + unique case (instr_i[6]) + 1'b0: begin + // c.lhu -> lhu rd', imm(rs1') + instr_o = {10'b0, instr_i[5], 1'b0, 2'b01, instr_i[9:7], + 3'b101, 2'b01, instr_i[4:2], {OPCODE_LOAD}}; + end + 1'b1: begin + // c.lh -> lh rd', imm(rs1') + instr_o = {10'b0, instr_i[5], 1'b0, 2'b01, instr_i[9:7], + 3'b001, 2'b01, instr_i[4:2], {OPCODE_LOAD}}; + end + + default: begin + illegal_instr_o = 1'b1; + end + endcase + end + + 3'b010: begin + // c.sb -> sb rs2', imm(rs1') + instr_o = {7'b0, 2'b01, instr_i[4:2], 2'b01, instr_i[9:7], + 3'b000, 3'b0, instr_i[5], instr_i[6], {OPCODE_STORE}}; + end + + 3'b011: begin + unique case (instr_i[6]) + 1'b0: begin + // c.sh -> sh rs2', imm(rs1') + // The instr_i[6] should always be zero according to the reference + instr_o = {7'b0, 2'b01, instr_i[4:2], 2'b01, instr_i[9:7], + 3'b001, 3'b0, instr_i[5], 1'b0, {OPCODE_STORE}}; + end + 1'b1: begin + illegal_instr_o = 1'b1; + end + + default: begin + illegal_instr_o = 1'b1; + end + endcase + end + + default: begin + illegal_instr_o = 1'b1; + end + endcase // unique case (instr_i[12:10]) + end else begin + // The Zcb extension is not enabled + illegal_instr_o = 1'b1; + end + end + 3'b001, 3'b011, - 3'b100, 3'b101, 3'b111: begin illegal_instr_o = 1'b1; @@ -165,14 +400,71 @@ module ibex_compressed_decoder ( end 3'b100, - 3'b101, - 3'b110, - 3'b111: begin + 3'b101: begin // 100: c.subw // 101: c.addw illegal_instr_o = 1'b1; end + 3'b110: begin + if (RV32ZC == RV32ZcaZcbZcmp || RV32ZC == RV32ZcaZcb) begin + // c.mul -> m.mul rsd', rsd', rs2' + instr_o = {7'b0000001, 2'b01, instr_i[4:2], 2'b01, instr_i[9:7], + 3'b000, 2'b01, instr_i[9:7], {OPCODE_OP}}; + end else begin + // The Zcb extension is not enabled + illegal_instr_o = 1'b1; + end + end + + 3'b111: begin + if (RV32ZC == RV32ZcaZcbZcmp || RV32ZC == RV32ZcaZcb) begin + unique case ({instr_i[4:2]}) + 3'b000: begin + // c.zext.b -> andi rsd', rsd', 8'hff + instr_o = {4'b0, 8'hff, 2'b01, instr_i[9:7], 3'b111, + 2'b01, instr_i[9:7], {OPCODE_OP_IMM}}; + end + + 3'b001: begin + // c.sext.b -> sext.b rsd', rsd' + instr_o = {7'b0110000, 5'b00100, 2'b01, instr_i[9:7], + 3'b001, 2'b01, instr_i[9:7], {OPCODE_OP_IMM}}; + end + + 3'b010: begin + // c.zext.h -> zext.h rsd', rsd' + instr_o = {7'b0000100, 5'b0, 2'b01, instr_i[9:7], + 3'b100, 2'b01, instr_i[9:7], {OPCODE_OP}}; + end + + 3'b011: begin + // c.sext.h -> sext.h rsd', rsd' + instr_o = {7'b0110000, 5'b00101, 2'b01, instr_i[9:7], + 3'b001, 2'b01, instr_i[9:7], {OPCODE_OP_IMM}}; + end + + 3'b100: begin + // c.zext.w -> add.uw: only valid instruction for RV64 cores + illegal_instr_o = 1'b1; + end + + 3'b101: begin + // c.not -> xori rsd', rsd', -1 + instr_o = {12'hfff, 2'b01, instr_i[9:7], 3'b100, + 2'b01, instr_i[9:7], {OPCODE_OP_IMM}}; + end + + default: begin + illegal_instr_o = 1'b1; + end + endcase + end else begin + // The Zcb extension is not enabled + illegal_instr_o = 1'b1; + end + end + default: begin illegal_instr_o = 1'b1; end @@ -248,6 +540,225 @@ module ibex_compressed_decoder ( end end + 3'b101: begin + if (RV32ZC == RV32ZcaZcbZcmp || RV32ZC == RV32ZcaZcmp) begin + unique casez (instr_i[12:8]) + // cm.push + 5'b11000: begin + // This compressed instruction gets expanded into multiple instructions. + gets_expanded = INSTR_EXPANDED; + unique case (cm_state_q) + CmIdle: begin + // No cm.push instruction is active yet; start a new one. + // Initialize `rlist` to the value provided by the instruction. + cm_rlist_d = cm_rlist_init(instr_i[7:4]); + // Store the register at the top of `rlist`, which is the highest register in + // the list. Then work our way down by decrementing `rlist` each cycle. + instr_o = cm_push_store_reg(.rlist(cm_rlist_d), .sp_offset(5'd1)); + if (cm_rlist_d <= 5'd3) begin + // Reserved --> illegal instruction. + illegal_instr_o = 1'b1; + end else if (cm_rlist_d == 5'd4) begin + // Only `ra` has to be stored, which is done in this cycle. Proceed by + // decrementing SP. + if (valid_i && id_in_ready_i) begin + cm_state_d = CmPushDecrSp; + end + end else begin + // More registers have to be stored. + // Remove the current register from `rlist`. + cm_rlist_d -= 5'd1; + // Initialize SP offset to 2. + // We just stored at offset 1 this cycle and will start incrementing the + // offset from 2 onwards in CmPushStoreReg next cycle. + cm_sp_offset_d = 5'd2; + // Proceed with storing registers. + if (valid_i && id_in_ready_i) begin + cm_state_d = CmPushStoreReg; + end + end + end + CmPushStoreReg: begin + // Store register at the top of current `rlist`. + instr_o = cm_push_store_reg(.rlist(cm_rlist_q), .sp_offset(cm_sp_offset_q)); + if (id_in_ready_i) begin + // Remove top register from `rlist`. + cm_rlist_d = cm_rlist_q - 5'd1; + // Increment the SP offset. + cm_sp_offset_d = cm_sp_offset_q + 5'd1; + if (cm_rlist_q == 5'd4) begin + // The last register gets stored in this cycle. Proceed by decrementing + // SP. + cm_state_d = CmPushDecrSp; + end + end + end + CmPushDecrSp: begin + // Decrement stack pointer. + instr_o = cm_sp_addi(.rlist(instr_i[7:4]), .spimm(instr_i[3:2]), .decr(1'b1)); + if (id_in_ready_i) begin + // This is the final operation, so stop expanding and return to idle. + gets_expanded = INSTR_EXPANDED_LAST; + cm_state_d = CmIdle; + end + end + default: cm_state_d = CmIdle; + endcase + end + + // cm.pop, cm.popretz, cm.popret + 5'b11010, + 5'b11100, + 5'b11110: begin + // This compressed instruction gets expanded into multiple instructions. + gets_expanded = INSTR_EXPANDED; + unique case (cm_state_q) + CmIdle: begin + // No cm.pop instruction is active yet; start a new one. + // Initialize `rlist` to the value provided by the instruction. + cm_rlist_d = cm_rlist_init(instr_i[7:4]); + // Initialize SP offset. + cm_sp_offset_d = cm_stack_adj_word(.rlist(instr_i[7:4]), + .spimm(instr_i[3:2])) - 5'd1; + // Load the register at the top of `rlist`, which is the highest register in + // the list. Then work our way down by decrementing `rlist` each cycle. + instr_o = cm_pop_load_reg(.rlist(cm_rlist_d), .sp_offset(cm_sp_offset_d)); + if (cm_rlist_d <= 5'd3) begin + // Reserved --> illegal instruction. + illegal_instr_o = 1'b1; + end else if (cm_rlist_d == 5'd4) begin + // Only `ra` has to be loaded, which is done in this cycle. Proceed by + // incrementing SP. + if (valid_i && id_in_ready_i) begin + cm_state_d = CmPopIncrSp; + end + end else begin + // More registers have to be loaded. + // Remove the current register from `rlist` and decrement the SP offset. + cm_rlist_d -= 5'd1; + cm_sp_offset_d -= 5'd1; + // Proceed with loading registers. + if (valid_i && id_in_ready_i) begin + cm_state_d = CmPopLoadReg; + end + end + end + CmPopLoadReg: begin + // Load register at the top of current `rlist`. + instr_o = cm_pop_load_reg(.rlist(cm_rlist_q), .sp_offset(cm_sp_offset_q)); + if (id_in_ready_i) begin + // Remove top register from `rlist`. + cm_rlist_d = cm_rlist_q - 5'd1; + // Decrement the SP offset. + cm_sp_offset_d = cm_sp_offset_q - 5'd1; + if (cm_rlist_q == 5'd4) begin + // The last register gets stored in this cycle. Proceed by incrementing + // SP. + cm_state_d = CmPopIncrSp; + end + end + end + CmPopIncrSp: begin + // Increment stack pointer. + instr_o = cm_sp_addi(.rlist(instr_i[7:4]), + .spimm(instr_i[3:2]), + .decr(1'b0)); + if (id_in_ready_i) begin + unique case (instr_i[12:8]) + 5'b11100: cm_state_d = CmPopZeroA0; // cm.popretz + 5'b11110: cm_state_d = CmPopRetRa; // cm.popret + default: begin // cm.pop + // This is the final operation, so stop expanding and return to idle. + gets_expanded = INSTR_EXPANDED_LAST; + cm_state_d = CmIdle; + end + endcase + end + end + CmPopZeroA0: begin + instr_o = cm_zero_a0(); + if (id_in_ready_i) begin + cm_state_d = CmPopRetRa; + end + end + CmPopRetRa: begin + instr_o = cm_ret_ra(); + if (id_in_ready_i) begin + // This is the final operation, so stop expanding and return to idle. + gets_expanded = INSTR_EXPANDED_LAST; + cm_state_d = CmIdle; + end + end + default: cm_state_d = CmIdle; + endcase + end + + // cm.mvsa01, cm.mva01s + 5'b011??: begin + unique case (instr_i[6:5]) + // cm.mvsa01 + 2'b01: begin + // This compressed instruction gets expanded into multiple instructions. + gets_expanded = INSTR_EXPANDED; + unique case (cm_state_q) + CmIdle: begin + // No cm.mvsa01 instruction is active yet; start a new one. + // Move a0 to register indicated by r1s'. + instr_o = cm_mvsa01(.a01(1'b0), .rs(instr_i[9:7])); + if (valid_i && id_in_ready_i) begin + cm_state_d = CmMvSecondReg; + end + end + CmMvSecondReg: begin + // Move a1 to register indicated by r2s'. + instr_o = cm_mvsa01(.a01(1'b1), .rs(instr_i[4:2])); + if (id_in_ready_i) begin + // This is the final operation, so stop expanding and return to idle. + gets_expanded = INSTR_EXPANDED_LAST; + cm_state_d = CmIdle; + end + end + default: cm_state_d = CmIdle; + endcase + end + + // cm.mva01s + 2'b11: begin + // This compressed instruction gets expanded into multiple instructions. + gets_expanded = INSTR_EXPANDED; + unique case (cm_state_q) + CmIdle: begin + // No cm.mva01s instruction is active yet; start a new one. + // Move register indicated by r1s' into a0. + instr_o = cm_mva01s(.rs(instr_i[9:7]), .a01(1'b0)); + if (valid_i && id_in_ready_i) begin + cm_state_d = CmMvSecondReg; + end + end + CmMvSecondReg: begin + // Move register indicated by r2s' into a1. + instr_o = cm_mva01s(.rs(instr_i[4:2]), .a01(1'b1)); + if (id_in_ready_i) begin + // This is the final operation, so stop expanding and return to idle. + gets_expanded = INSTR_EXPANDED_LAST; + cm_state_d = CmIdle; + end + end + default: cm_state_d = CmIdle; + endcase + end + default: illegal_instr_o = 1'b1; + endcase + end + + default: illegal_instr_o = 1'b1; + endcase + end else begin + // The Zcmp extension is not enabled + illegal_instr_o = 1'b1; + end + end + 3'b110: begin // c.swsp -> sw rs2, imm(x2) instr_o = {4'b0, instr_i[8:7], instr_i[12], instr_i[6:2], 5'h02, 3'b010, @@ -256,7 +767,6 @@ module ibex_compressed_decoder ( 3'b001, 3'b011, - 3'b101, 3'b111: begin illegal_instr_o = 1'b1; end @@ -278,6 +788,32 @@ module ibex_compressed_decoder ( assign is_compressed_o = (instr_i[1:0] != 2'b11); + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + cm_state_q <= CmIdle; + end else begin + cm_state_q <= cm_state_d; + end + end + + if (ResetAll) begin : g_cm_meta_ra + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + cm_rlist_q <= '0; + cm_sp_offset_q <= '0; + end else begin + cm_rlist_q <= cm_rlist_d; + cm_sp_offset_q <= cm_sp_offset_d; + end + end + end else begin : g_cm_meta_nr + // The following regs don't need to be reset as they get assigned before first usage: + always_ff @(posedge clk_i) begin + cm_rlist_q <= cm_rlist_d; + cm_sp_offset_q <= cm_sp_offset_d; + end + end + //////////////// // Assertions // //////////////// @@ -299,5 +835,6 @@ module ibex_compressed_decoder ( !$isunknown({instr_i[12], instr_i[6:5]})) `ASSERT(IbexC2Known1, (valid_i && (instr_i[1:0] == 2'b10)) |-> !$isunknown(instr_i[15:13])) + `ASSERT(IbexPushPopFSMStable, !valid_i |-> cm_state_d == cm_state_q) endmodule diff --git a/rtl/ibex_core.sv b/rtl/ibex_core.sv index 56a59de5fd..f4b9d4c3cf 100644 --- a/rtl/ibex_core.sv +++ b/rtl/ibex_core.sv @@ -25,6 +25,7 @@ module ibex_core import ibex_pkg::*; #( parameter bit RV32E = 1'b0, parameter rv32m_e RV32M = RV32MFast, parameter rv32b_e RV32B = RV32BNone, + parameter rv32zc_e RV32ZC = RV32ZcaZcbZcmp, parameter bit BranchTargetALU = 1'b0, parameter bit WritebackStage = 1'b0, parameter bit ICache = 1'b0, @@ -158,6 +159,9 @@ module ibex_core import ibex_pkg::*; #( output logic [31:0] rvfi_ext_mhpmcountersh [10], output logic rvfi_ext_ic_scr_key_valid, output logic rvfi_ext_irq_valid, + output logic rvfi_ext_expanded_insn_valid, + output logic [15:0] rvfi_ext_expanded_insn, + output logic rvfi_ext_expanded_insn_last, `endif // CPU Control Signals @@ -184,6 +188,8 @@ module ibex_core import ibex_pkg::*; #( // ease fan-out) logic [15:0] instr_rdata_c_id; // Compressed instruction sampled inside IF stage logic instr_is_compressed_id; + instr_exp_e instr_gets_expanded_id; + logic [15:0] instr_expanded_id; logic instr_perf_count_id; logic instr_bp_taken_id; logic instr_fetch_err; // Bus error on instr fetch @@ -423,6 +429,7 @@ module ibex_core import ibex_pkg::*; #( .DmExceptionAddr (DmExceptionAddr), .DummyInstructions(DummyInstructions), .ICache (ICache), + .RV32ZC (RV32ZC), .ICacheECC (ICacheECC), .BusSizeECC (BusSizeECC), .TagSizeECC (TagSizeECC), @@ -470,6 +477,8 @@ module ibex_core import ibex_pkg::*; #( .instr_rdata_alu_id_o (instr_rdata_alu_id), .instr_rdata_c_id_o (instr_rdata_c_id), .instr_is_compressed_id_o(instr_is_compressed_id), + .instr_gets_expanded_id_o(instr_gets_expanded_id), + .instr_expanded_id_o (instr_expanded_id), .instr_bp_taken_o (instr_bp_taken_id), .instr_fetch_err_o (instr_fetch_err), .instr_fetch_err_plus2_o (instr_fetch_err_plus2), @@ -1296,17 +1305,24 @@ module ibex_core import ibex_pkg::*; #( // RVFI extension for co-simulation support // debug_req and MIP captured at IF -> ID transition so one extra stage - ibex_pkg::irqs_t rvfi_ext_stage_pre_mip [RVFI_STAGES+1]; - ibex_pkg::irqs_t rvfi_ext_stage_post_mip [RVFI_STAGES]; - logic rvfi_ext_stage_nmi [RVFI_STAGES+1]; - logic rvfi_ext_stage_nmi_int [RVFI_STAGES+1]; - logic rvfi_ext_stage_debug_req [RVFI_STAGES+1]; - logic rvfi_ext_stage_debug_mode [RVFI_STAGES]; - logic [63:0] rvfi_ext_stage_mcycle [RVFI_STAGES]; - logic [31:0] rvfi_ext_stage_mhpmcounters [RVFI_STAGES][10]; - logic [31:0] rvfi_ext_stage_mhpmcountersh [RVFI_STAGES][10]; - logic rvfi_ext_stage_ic_scr_key_valid [RVFI_STAGES]; - logic rvfi_ext_stage_irq_valid [RVFI_STAGES+1]; + ibex_pkg::irqs_t rvfi_ext_stage_pre_mip [RVFI_STAGES+1]; + ibex_pkg::irqs_t rvfi_ext_stage_post_mip [RVFI_STAGES]; + logic rvfi_ext_stage_nmi [RVFI_STAGES+1]; + logic rvfi_ext_stage_nmi_int [RVFI_STAGES+1]; + logic rvfi_ext_stage_debug_req [RVFI_STAGES+1]; + logic rvfi_ext_stage_debug_mode [RVFI_STAGES]; + logic [63:0] rvfi_ext_stage_mcycle [RVFI_STAGES]; + logic [31:0] rvfi_ext_stage_mhpmcounters [RVFI_STAGES][10]; + logic [31:0] rvfi_ext_stage_mhpmcountersh [RVFI_STAGES][10]; + logic rvfi_ext_stage_ic_scr_key_valid [RVFI_STAGES]; + logic rvfi_ext_stage_irq_valid [RVFI_STAGES+1]; + logic rvfi_ext_stage_expanded_insn_valid [RVFI_STAGES]; + logic [15:0] rvfi_ext_stage_expanded_insn [RVFI_STAGES]; + logic rvfi_ext_stage_expanded_insn_last [RVFI_STAGES]; + + logic rvfi_expanded_insn_valid; + logic [15:0] rvfi_expanded_insn; + logic rvfi_expanded_insn_last; logic rvfi_stage_valid_d [RVFI_STAGES]; @@ -1359,15 +1375,18 @@ module ibex_core import ibex_pkg::*; #( rvfi_ext_stage_post_mip[RVFI_STAGES-1].irq_fast; end - assign rvfi_ext_nmi = rvfi_ext_stage_nmi [RVFI_STAGES]; - assign rvfi_ext_nmi_int = rvfi_ext_stage_nmi_int [RVFI_STAGES]; - assign rvfi_ext_debug_req = rvfi_ext_stage_debug_req [RVFI_STAGES]; - assign rvfi_ext_debug_mode = rvfi_ext_stage_debug_mode [RVFI_STAGES-1]; - assign rvfi_ext_mcycle = rvfi_ext_stage_mcycle [RVFI_STAGES-1]; - assign rvfi_ext_mhpmcounters = rvfi_ext_stage_mhpmcounters [RVFI_STAGES-1]; - assign rvfi_ext_mhpmcountersh = rvfi_ext_stage_mhpmcountersh [RVFI_STAGES-1]; - assign rvfi_ext_ic_scr_key_valid = rvfi_ext_stage_ic_scr_key_valid [RVFI_STAGES-1]; - assign rvfi_ext_irq_valid = rvfi_ext_stage_irq_valid [RVFI_STAGES]; + assign rvfi_ext_nmi = rvfi_ext_stage_nmi [RVFI_STAGES]; + assign rvfi_ext_nmi_int = rvfi_ext_stage_nmi_int [RVFI_STAGES]; + assign rvfi_ext_debug_req = rvfi_ext_stage_debug_req [RVFI_STAGES]; + assign rvfi_ext_debug_mode = rvfi_ext_stage_debug_mode [RVFI_STAGES-1]; + assign rvfi_ext_mcycle = rvfi_ext_stage_mcycle [RVFI_STAGES-1]; + assign rvfi_ext_mhpmcounters = rvfi_ext_stage_mhpmcounters [RVFI_STAGES-1]; + assign rvfi_ext_mhpmcountersh = rvfi_ext_stage_mhpmcountersh [RVFI_STAGES-1]; + assign rvfi_ext_ic_scr_key_valid = rvfi_ext_stage_ic_scr_key_valid [RVFI_STAGES-1]; + assign rvfi_ext_irq_valid = rvfi_ext_stage_irq_valid [RVFI_STAGES]; + assign rvfi_ext_expanded_insn_valid = rvfi_ext_stage_expanded_insn_valid [RVFI_STAGES-1]; + assign rvfi_ext_expanded_insn = rvfi_ext_stage_expanded_insn [RVFI_STAGES-1]; + assign rvfi_ext_expanded_insn_last = rvfi_ext_stage_expanded_insn_last [RVFI_STAGES-1]; // When an instruction takes a trap the `rvfi_trap` signal will be set. Instructions that take // traps flush the pipeline so ordinarily wouldn't be seen to be retire. The RVFI tracking @@ -1554,109 +1573,115 @@ module ibex_core import ibex_pkg::*; #( for (genvar i = 0; i < RVFI_STAGES; i = i + 1) begin : g_rvfi_stages always_ff @(posedge clk_i or negedge rst_ni) begin if (!rst_ni) begin - rvfi_stage_halt[i] <= '0; - rvfi_stage_trap[i] <= '0; - rvfi_stage_intr[i] <= '0; - rvfi_stage_order[i] <= '0; - rvfi_stage_insn[i] <= '0; - rvfi_stage_mode[i] <= {PRIV_LVL_M}; - rvfi_stage_ixl[i] <= CSR_MISA_MXL; - rvfi_stage_rs1_addr[i] <= '0; - rvfi_stage_rs2_addr[i] <= '0; - rvfi_stage_rs3_addr[i] <= '0; - rvfi_stage_pc_rdata[i] <= '0; - rvfi_stage_pc_wdata[i] <= '0; - rvfi_stage_mem_rmask[i] <= '0; - rvfi_stage_mem_wmask[i] <= '0; - rvfi_stage_valid[i] <= '0; - rvfi_stage_rs1_rdata[i] <= '0; - rvfi_stage_rs2_rdata[i] <= '0; - rvfi_stage_rs3_rdata[i] <= '0; - rvfi_stage_rd_wdata[i] <= '0; - rvfi_stage_rd_addr[i] <= '0; - rvfi_stage_mem_rdata[i] <= '0; - rvfi_stage_mem_wdata[i] <= '0; - rvfi_stage_mem_addr[i] <= '0; - rvfi_ext_stage_pre_mip[i+1] <= '0; - rvfi_ext_stage_post_mip[i] <= '0; - rvfi_ext_stage_nmi[i+1] <= '0; - rvfi_ext_stage_nmi_int[i+1] <= '0; - rvfi_ext_stage_debug_req[i+1] <= '0; - rvfi_ext_stage_debug_mode[i] <= '0; - rvfi_ext_stage_mcycle[i] <= '0; - rvfi_ext_stage_ic_scr_key_valid[i] <= '0; + rvfi_stage_halt[i] <= '0; + rvfi_stage_trap[i] <= '0; + rvfi_stage_intr[i] <= '0; + rvfi_stage_order[i] <= '0; + rvfi_stage_insn[i] <= '0; + rvfi_stage_mode[i] <= {PRIV_LVL_M}; + rvfi_stage_ixl[i] <= CSR_MISA_MXL; + rvfi_stage_rs1_addr[i] <= '0; + rvfi_stage_rs2_addr[i] <= '0; + rvfi_stage_rs3_addr[i] <= '0; + rvfi_stage_pc_rdata[i] <= '0; + rvfi_stage_pc_wdata[i] <= '0; + rvfi_stage_mem_rmask[i] <= '0; + rvfi_stage_mem_wmask[i] <= '0; + rvfi_stage_valid[i] <= '0; + rvfi_stage_rs1_rdata[i] <= '0; + rvfi_stage_rs2_rdata[i] <= '0; + rvfi_stage_rs3_rdata[i] <= '0; + rvfi_stage_rd_wdata[i] <= '0; + rvfi_stage_rd_addr[i] <= '0; + rvfi_stage_mem_rdata[i] <= '0; + rvfi_stage_mem_wdata[i] <= '0; + rvfi_stage_mem_addr[i] <= '0; + rvfi_ext_stage_pre_mip[i+1] <= '0; + rvfi_ext_stage_post_mip[i] <= '0; + rvfi_ext_stage_nmi[i+1] <= '0; + rvfi_ext_stage_nmi_int[i+1] <= '0; + rvfi_ext_stage_debug_req[i+1] <= '0; + rvfi_ext_stage_debug_mode[i] <= '0; + rvfi_ext_stage_mcycle[i] <= '0; + rvfi_ext_stage_ic_scr_key_valid[i] <= '0; + rvfi_ext_stage_expanded_insn_valid[i] <= '0; + rvfi_ext_stage_expanded_insn[i] <= '0; + rvfi_ext_stage_expanded_insn_last[i] <= '0; // DSim does not properly support array assignment in for loop, so unroll - rvfi_ext_stage_mhpmcounters[i][0] <= '0; - rvfi_ext_stage_mhpmcountersh[i][0] <= '0; - rvfi_ext_stage_mhpmcounters[i][1] <= '0; - rvfi_ext_stage_mhpmcountersh[i][1] <= '0; - rvfi_ext_stage_mhpmcounters[i][2] <= '0; - rvfi_ext_stage_mhpmcountersh[i][2] <= '0; - rvfi_ext_stage_mhpmcounters[i][3] <= '0; - rvfi_ext_stage_mhpmcountersh[i][3] <= '0; - rvfi_ext_stage_mhpmcounters[i][4] <= '0; - rvfi_ext_stage_mhpmcountersh[i][4] <= '0; - rvfi_ext_stage_mhpmcounters[i][5] <= '0; - rvfi_ext_stage_mhpmcountersh[i][5] <= '0; - rvfi_ext_stage_mhpmcounters[i][6] <= '0; - rvfi_ext_stage_mhpmcountersh[i][6] <= '0; - rvfi_ext_stage_mhpmcounters[i][7] <= '0; - rvfi_ext_stage_mhpmcountersh[i][7] <= '0; - rvfi_ext_stage_mhpmcounters[i][8] <= '0; - rvfi_ext_stage_mhpmcountersh[i][8] <= '0; - rvfi_ext_stage_mhpmcounters[i][9] <= '0; - rvfi_ext_stage_mhpmcountersh[i][9] <= '0; + rvfi_ext_stage_mhpmcounters[i][0] <= '0; + rvfi_ext_stage_mhpmcountersh[i][0] <= '0; + rvfi_ext_stage_mhpmcounters[i][1] <= '0; + rvfi_ext_stage_mhpmcountersh[i][1] <= '0; + rvfi_ext_stage_mhpmcounters[i][2] <= '0; + rvfi_ext_stage_mhpmcountersh[i][2] <= '0; + rvfi_ext_stage_mhpmcounters[i][3] <= '0; + rvfi_ext_stage_mhpmcountersh[i][3] <= '0; + rvfi_ext_stage_mhpmcounters[i][4] <= '0; + rvfi_ext_stage_mhpmcountersh[i][4] <= '0; + rvfi_ext_stage_mhpmcounters[i][5] <= '0; + rvfi_ext_stage_mhpmcountersh[i][5] <= '0; + rvfi_ext_stage_mhpmcounters[i][6] <= '0; + rvfi_ext_stage_mhpmcountersh[i][6] <= '0; + rvfi_ext_stage_mhpmcounters[i][7] <= '0; + rvfi_ext_stage_mhpmcountersh[i][7] <= '0; + rvfi_ext_stage_mhpmcounters[i][8] <= '0; + rvfi_ext_stage_mhpmcountersh[i][8] <= '0; + rvfi_ext_stage_mhpmcounters[i][9] <= '0; + rvfi_ext_stage_mhpmcountersh[i][9] <= '0; end else begin rvfi_stage_valid[i] <= rvfi_stage_valid_d[i]; if (i == 0) begin if (rvfi_id_done) begin - rvfi_stage_halt[i] <= '0; - rvfi_stage_trap[i] <= rvfi_trap_id; - rvfi_stage_intr[i] <= rvfi_intr_d; - rvfi_stage_order[i] <= rvfi_stage_order_d; - rvfi_stage_insn[i] <= rvfi_insn_id; - rvfi_stage_mode[i] <= {priv_mode_id}; - rvfi_stage_ixl[i] <= CSR_MISA_MXL; - rvfi_stage_rs1_addr[i] <= rvfi_rs1_addr_d; - rvfi_stage_rs2_addr[i] <= rvfi_rs2_addr_d; - rvfi_stage_rs3_addr[i] <= rvfi_rs3_addr_d; - rvfi_stage_pc_rdata[i] <= pc_id; - rvfi_stage_pc_wdata[i] <= pc_set ? branch_target_ex : pc_if; - rvfi_stage_mem_rmask[i] <= data_we_o ? 4'b0000 : rvfi_mem_mask_int; - rvfi_stage_mem_wmask[i] <= data_we_o ? rvfi_mem_mask_int : 4'b0000; - rvfi_stage_rs1_rdata[i] <= rvfi_rs1_data_d; - rvfi_stage_rs2_rdata[i] <= rvfi_rs2_data_d; - rvfi_stage_rs3_rdata[i] <= rvfi_rs3_data_d; - rvfi_stage_rd_addr[i] <= rvfi_rd_addr_d; - rvfi_stage_rd_wdata[i] <= rvfi_rd_wdata_d; - rvfi_stage_mem_rdata[i] <= rvfi_mem_rdata_d; - rvfi_stage_mem_wdata[i] <= rvfi_mem_wdata_d; - rvfi_stage_mem_addr[i] <= rvfi_mem_addr_d; - rvfi_ext_stage_debug_mode[i] <= debug_mode; - rvfi_ext_stage_mcycle[i] <= cs_registers_i.mcycle_counter_i.counter_val_o; - rvfi_ext_stage_ic_scr_key_valid[i] <= cs_registers_i.cpuctrlsts_ic_scr_key_valid_q; + rvfi_stage_halt[i] <= '0; + rvfi_stage_trap[i] <= rvfi_trap_id; + rvfi_stage_intr[i] <= rvfi_intr_d; + rvfi_stage_order[i] <= rvfi_stage_order_d; + rvfi_stage_insn[i] <= rvfi_insn_id; + rvfi_stage_mode[i] <= {priv_mode_id}; + rvfi_stage_ixl[i] <= CSR_MISA_MXL; + rvfi_stage_rs1_addr[i] <= rvfi_rs1_addr_d; + rvfi_stage_rs2_addr[i] <= rvfi_rs2_addr_d; + rvfi_stage_rs3_addr[i] <= rvfi_rs3_addr_d; + rvfi_stage_pc_rdata[i] <= pc_id; + rvfi_stage_pc_wdata[i] <= pc_set ? branch_target_ex : pc_if; + rvfi_stage_mem_rmask[i] <= data_we_o ? 4'b0000 : rvfi_mem_mask_int; + rvfi_stage_mem_wmask[i] <= data_we_o ? rvfi_mem_mask_int : 4'b0000; + rvfi_stage_rs1_rdata[i] <= rvfi_rs1_data_d; + rvfi_stage_rs2_rdata[i] <= rvfi_rs2_data_d; + rvfi_stage_rs3_rdata[i] <= rvfi_rs3_data_d; + rvfi_stage_rd_addr[i] <= rvfi_rd_addr_d; + rvfi_stage_rd_wdata[i] <= rvfi_rd_wdata_d; + rvfi_stage_mem_rdata[i] <= rvfi_mem_rdata_d; + rvfi_stage_mem_wdata[i] <= rvfi_mem_wdata_d; + rvfi_stage_mem_addr[i] <= rvfi_mem_addr_d; + rvfi_ext_stage_debug_mode[i] <= debug_mode; + rvfi_ext_stage_mcycle[i] <= cs_registers_i.mcycle_counter_i.counter_val_o; + rvfi_ext_stage_ic_scr_key_valid[i] <= cs_registers_i.cpuctrlsts_ic_scr_key_valid_q; + rvfi_ext_stage_expanded_insn_valid[i] <= rvfi_expanded_insn_valid; + rvfi_ext_stage_expanded_insn[i] <= rvfi_expanded_insn; + rvfi_ext_stage_expanded_insn_last[i] <= rvfi_expanded_insn_last; // DSim does not properly support array assignment in for loop, so unroll - rvfi_ext_stage_mhpmcounters[i][0] <= cs_registers_i.mhpmcounter[3][31:0]; - rvfi_ext_stage_mhpmcountersh[i][0] <= cs_registers_i.mhpmcounter[3][63:32]; - rvfi_ext_stage_mhpmcounters[i][1] <= cs_registers_i.mhpmcounter[4][31:0]; - rvfi_ext_stage_mhpmcountersh[i][1] <= cs_registers_i.mhpmcounter[4][63:32]; - rvfi_ext_stage_mhpmcounters[i][2] <= cs_registers_i.mhpmcounter[5][31:0]; - rvfi_ext_stage_mhpmcountersh[i][2] <= cs_registers_i.mhpmcounter[5][63:32]; - rvfi_ext_stage_mhpmcounters[i][3] <= cs_registers_i.mhpmcounter[6][31:0]; - rvfi_ext_stage_mhpmcountersh[i][3] <= cs_registers_i.mhpmcounter[6][63:32]; - rvfi_ext_stage_mhpmcounters[i][4] <= cs_registers_i.mhpmcounter[7][31:0]; - rvfi_ext_stage_mhpmcountersh[i][4] <= cs_registers_i.mhpmcounter[7][63:32]; - rvfi_ext_stage_mhpmcounters[i][5] <= cs_registers_i.mhpmcounter[8][31:0]; - rvfi_ext_stage_mhpmcountersh[i][5] <= cs_registers_i.mhpmcounter[8][63:32]; - rvfi_ext_stage_mhpmcounters[i][6] <= cs_registers_i.mhpmcounter[9][31:0]; - rvfi_ext_stage_mhpmcountersh[i][6] <= cs_registers_i.mhpmcounter[9][63:32]; - rvfi_ext_stage_mhpmcounters[i][7] <= cs_registers_i.mhpmcounter[10][31:0]; - rvfi_ext_stage_mhpmcountersh[i][7] <= cs_registers_i.mhpmcounter[10][63:32]; - rvfi_ext_stage_mhpmcounters[i][8] <= cs_registers_i.mhpmcounter[11][31:0]; - rvfi_ext_stage_mhpmcountersh[i][8] <= cs_registers_i.mhpmcounter[11][63:32]; - rvfi_ext_stage_mhpmcounters[i][9] <= cs_registers_i.mhpmcounter[12][31:0]; - rvfi_ext_stage_mhpmcountersh[i][9] <= cs_registers_i.mhpmcounter[12][63:32]; + rvfi_ext_stage_mhpmcounters[i][0] <= cs_registers_i.mhpmcounter[3][31:0]; + rvfi_ext_stage_mhpmcountersh[i][0] <= cs_registers_i.mhpmcounter[3][63:32]; + rvfi_ext_stage_mhpmcounters[i][1] <= cs_registers_i.mhpmcounter[4][31:0]; + rvfi_ext_stage_mhpmcountersh[i][1] <= cs_registers_i.mhpmcounter[4][63:32]; + rvfi_ext_stage_mhpmcounters[i][2] <= cs_registers_i.mhpmcounter[5][31:0]; + rvfi_ext_stage_mhpmcountersh[i][2] <= cs_registers_i.mhpmcounter[5][63:32]; + rvfi_ext_stage_mhpmcounters[i][3] <= cs_registers_i.mhpmcounter[6][31:0]; + rvfi_ext_stage_mhpmcountersh[i][3] <= cs_registers_i.mhpmcounter[6][63:32]; + rvfi_ext_stage_mhpmcounters[i][4] <= cs_registers_i.mhpmcounter[7][31:0]; + rvfi_ext_stage_mhpmcountersh[i][4] <= cs_registers_i.mhpmcounter[7][63:32]; + rvfi_ext_stage_mhpmcounters[i][5] <= cs_registers_i.mhpmcounter[8][31:0]; + rvfi_ext_stage_mhpmcountersh[i][5] <= cs_registers_i.mhpmcounter[8][63:32]; + rvfi_ext_stage_mhpmcounters[i][6] <= cs_registers_i.mhpmcounter[9][31:0]; + rvfi_ext_stage_mhpmcountersh[i][6] <= cs_registers_i.mhpmcounter[9][63:32]; + rvfi_ext_stage_mhpmcounters[i][7] <= cs_registers_i.mhpmcounter[10][31:0]; + rvfi_ext_stage_mhpmcountersh[i][7] <= cs_registers_i.mhpmcounter[10][63:32]; + rvfi_ext_stage_mhpmcounters[i][8] <= cs_registers_i.mhpmcounter[11][31:0]; + rvfi_ext_stage_mhpmcountersh[i][8] <= cs_registers_i.mhpmcounter[11][63:32]; + rvfi_ext_stage_mhpmcounters[i][9] <= cs_registers_i.mhpmcounter[12][31:0]; + rvfi_ext_stage_mhpmcountersh[i][9] <= cs_registers_i.mhpmcounter[12][63:32]; end // Some of the rvfi_ext_* signals are used to provide an interrupt notification (signalled @@ -1700,11 +1725,14 @@ module ibex_core import ibex_pkg::*; #( rvfi_stage_rd_wdata[i] <= rvfi_rd_wdata_d; rvfi_stage_mem_rdata[i] <= rvfi_mem_rdata_d; - rvfi_ext_stage_debug_mode[i] <= rvfi_ext_stage_debug_mode[i-1]; - rvfi_ext_stage_mcycle[i] <= rvfi_ext_stage_mcycle[i-1]; - rvfi_ext_stage_ic_scr_key_valid[i] <= rvfi_ext_stage_ic_scr_key_valid[i-1]; - rvfi_ext_stage_mhpmcounters[i] <= rvfi_ext_stage_mhpmcounters[i-1]; - rvfi_ext_stage_mhpmcountersh[i] <= rvfi_ext_stage_mhpmcountersh[i-1]; + rvfi_ext_stage_debug_mode[i] <= rvfi_ext_stage_debug_mode[i-1]; + rvfi_ext_stage_mcycle[i] <= rvfi_ext_stage_mcycle[i-1]; + rvfi_ext_stage_ic_scr_key_valid[i] <= rvfi_ext_stage_ic_scr_key_valid[i-1]; + rvfi_ext_stage_mhpmcounters[i] <= rvfi_ext_stage_mhpmcounters[i-1]; + rvfi_ext_stage_mhpmcountersh[i] <= rvfi_ext_stage_mhpmcountersh[i-1]; + rvfi_ext_stage_expanded_insn_valid[i] <= rvfi_ext_stage_expanded_insn_valid[i-1]; + rvfi_ext_stage_expanded_insn[i] <= rvfi_ext_stage_expanded_insn[i-1]; + rvfi_ext_stage_expanded_insn_last[i] <= rvfi_ext_stage_expanded_insn_last[i-1]; end // Some of the rvfi_ext_* signals are used to provide an interrupt notification (signalled @@ -1766,13 +1794,26 @@ module ibex_core import ibex_pkg::*; #( end always_comb begin - if (instr_is_compressed_id) begin + if (instr_is_compressed_id && (instr_gets_expanded_id == INSTR_NOT_EXPANDED)) begin rvfi_insn_id = {16'b0, instr_rdata_c_id}; end else begin rvfi_insn_id = instr_rdata_id; end end + always_comb begin + rvfi_expanded_insn_valid = 1'b0; + rvfi_expanded_insn = '0; + rvfi_expanded_insn_last = 1'b0; + if (instr_gets_expanded_id != INSTR_NOT_EXPANDED) begin + rvfi_expanded_insn_valid = 1'b1; + rvfi_expanded_insn = instr_expanded_id; + if (instr_gets_expanded_id == INSTR_EXPANDED_LAST) begin + rvfi_expanded_insn_last = 1'b1; + end + end + end + // Source registers 1 and 2 are read in the first instruction cycle // Source register 3 is read in the second instruction cycle. always_comb begin diff --git a/rtl/ibex_if_stage.sv b/rtl/ibex_if_stage.sv index ebdd6e46c4..ba5c0db53e 100644 --- a/rtl/ibex_if_stage.sv +++ b/rtl/ibex_if_stage.sv @@ -18,6 +18,7 @@ module ibex_if_stage import ibex_pkg::*; #( parameter int unsigned DmExceptionAddr = 32'h1A110808, parameter bit DummyInstructions = 1'b0, parameter bit ICache = 1'b0, + parameter rv32zc_e RV32ZC = RV32ZcaZcbZcmp, parameter bit ICacheECC = 1'b0, parameter int unsigned BusSizeECC = BUS_SIZE, parameter int unsigned TagSizeECC = IC_TAG_SIZE, @@ -70,6 +71,11 @@ module ibex_if_stage import ibex_pkg::*; #( // instr_is_compressed_id_o = 1'b1 output logic instr_is_compressed_id_o, // compressed decoder thinks this // is a compressed instr + output instr_exp_e instr_gets_expanded_id_o, // this instruction comes from one + // that gets expanded by the + // compressed decoder + output logic [15:0] instr_expanded_id_o, // the instruction that is currently + // getting expanded output logic instr_bp_taken_o, // instruction was predicted to be // a taken branch output logic instr_fetch_err_o, // bus error on fetch @@ -144,6 +150,7 @@ module ibex_if_stage import ibex_pkg::*; #( logic [31:0] instr_decompressed; logic illegal_c_insn; logic instr_is_compressed; + instr_exp_e instr_gets_expanded; logic if_instr_valid; logic [31:0] if_instr_rdata; @@ -161,6 +168,7 @@ module ibex_if_stage import ibex_pkg::*; #( logic stall_dummy_instr; logic [31:0] instr_out; logic instr_is_compressed_out; + instr_exp_e instr_gets_expanded_out; logic illegal_c_instr_out; logic instr_err_out; @@ -401,13 +409,18 @@ module ibex_if_stage import ibex_pkg::*; #( // // since it does not matter where we decompress instructions, we do it here // to ease timing closure - ibex_compressed_decoder compressed_decoder_i ( + ibex_compressed_decoder #( + .RV32ZC (RV32ZC), + .ResetAll (ResetAll) + ) compressed_decoder_i ( .clk_i (clk_i), .rst_ni (rst_ni), .valid_i (fetch_valid & ~fetch_err), + .id_in_ready_i (id_in_ready_i & ~pc_set_i), .instr_i (if_instr_rdata), .instr_o (instr_decompressed), .is_compressed_o(instr_is_compressed), + .gets_expanded_o(instr_gets_expanded), .illegal_instr_o(illegal_c_insn) ); @@ -436,6 +449,7 @@ module ibex_if_stage import ibex_pkg::*; #( // Mux between actual instructions and dummy instructions assign instr_out = insert_dummy_instr ? dummy_instr_data : instr_decompressed; assign instr_is_compressed_out = insert_dummy_instr ? 1'b0 : instr_is_compressed; + assign instr_gets_expanded_out = insert_dummy_instr ? INSTR_NOT_EXPANDED : instr_gets_expanded; assign illegal_c_instr_out = insert_dummy_instr ? 1'b0 : illegal_c_insn; assign instr_err_out = insert_dummy_instr ? 1'b0 : if_instr_err; @@ -465,6 +479,7 @@ module ibex_if_stage import ibex_pkg::*; #( assign unused_dummy_seed = dummy_instr_seed_i; assign instr_out = instr_decompressed; assign instr_is_compressed_out = instr_is_compressed; + assign instr_gets_expanded_out = instr_gets_expanded; assign illegal_c_instr_out = illegal_c_insn; assign instr_err_out = if_instr_err; assign stall_dummy_instr = 1'b0; @@ -476,7 +491,7 @@ module ibex_if_stage import ibex_pkg::*; #( // Valid is held until it is explicitly cleared (due to an instruction completing or an exception) assign instr_valid_id_d = (if_instr_valid & id_in_ready_i & ~pc_set_i) | (instr_valid_id_q & ~instr_valid_clear_i); - assign instr_new_id_d = if_instr_valid & id_in_ready_i; + assign instr_new_id_d = if_instr_valid & id_in_ready_i & ~pc_set_i; always_ff @(posedge clk_i or negedge rst_ni) begin if (!rst_ni) begin @@ -504,6 +519,8 @@ module ibex_if_stage import ibex_pkg::*; #( instr_fetch_err_plus2_o <= '0; instr_rdata_c_id_o <= '0; instr_is_compressed_id_o <= '0; + instr_gets_expanded_id_o <= INSTR_NOT_EXPANDED; + instr_expanded_id_o <= '0; illegal_c_insn_id_o <= '0; pc_id_o <= '0; end else if (if_id_pipe_reg_we) begin @@ -514,6 +531,8 @@ module ibex_if_stage import ibex_pkg::*; #( instr_fetch_err_plus2_o <= if_instr_err_plus2; instr_rdata_c_id_o <= if_instr_rdata[15:0]; instr_is_compressed_id_o <= instr_is_compressed_out; + instr_gets_expanded_id_o <= instr_gets_expanded_out; + instr_expanded_id_o <= if_instr_rdata[15:0]; illegal_c_insn_id_o <= illegal_c_instr_out; pc_id_o <= pc_if_o; end @@ -528,6 +547,8 @@ module ibex_if_stage import ibex_pkg::*; #( instr_fetch_err_plus2_o <= if_instr_err_plus2; instr_rdata_c_id_o <= if_instr_rdata[15:0]; instr_is_compressed_id_o <= instr_is_compressed_out; + instr_gets_expanded_id_o <= instr_gets_expanded_out; + instr_expanded_id_o <= if_instr_rdata[15:0]; illegal_c_insn_id_o <= illegal_c_instr_out; pc_id_o <= pc_if_o; end @@ -544,7 +565,7 @@ module ibex_if_stage import ibex_pkg::*; #( // request, all of which will set branch_req. Also do not check after reset or for dummy // instructions. assign prev_instr_seq_d = (prev_instr_seq_q | instr_new_id_d) & - ~branch_req & ~if_instr_err & ~stall_dummy_instr; + ~branch_req & ~if_instr_err & ~stall_dummy_instr & !(instr_gets_expanded == INSTR_EXPANDED); always_ff @(posedge clk_i or negedge rst_ni) begin if (!rst_ni) begin @@ -606,8 +627,8 @@ module ibex_if_stage import ibex_pkg::*; #( assign instr_skid_en = predict_branch_taken & ~pc_set_i & ~id_in_ready_i & ~instr_skid_valid_q; - assign instr_skid_valid_d = (instr_skid_valid_q & ~id_in_ready_i & ~stall_dummy_instr) | - instr_skid_en; + assign instr_skid_valid_d = (instr_skid_valid_q & ~id_in_ready_i & ~stall_dummy_instr & + !(instr_gets_expanded == INSTR_EXPANDED)) | instr_skid_en; always_ff @(posedge clk_i or negedge rst_ni) begin if (!rst_ni) begin @@ -665,7 +686,8 @@ module ibex_if_stage import ibex_pkg::*; #( assign if_instr_bus_err = ~instr_skid_valid_q & fetch_err; assign instr_bp_taken_d = instr_skid_valid_q ? instr_skid_bp_taken_q : predict_branch_taken; - assign fetch_ready = id_in_ready_i & ~stall_dummy_instr & ~instr_skid_valid_q; + assign fetch_ready = id_in_ready_i & ~stall_dummy_instr & + !(instr_gets_expanded == INSTR_EXPANDED) & ~instr_skid_valid_q; assign instr_bp_taken_o = instr_bp_taken_q; @@ -680,7 +702,8 @@ module ibex_if_stage import ibex_pkg::*; #( assign if_instr_rdata = fetch_rdata; assign if_instr_addr = fetch_addr; assign if_instr_bus_err = fetch_err; - assign fetch_ready = id_in_ready_i & ~stall_dummy_instr; + assign fetch_ready = id_in_ready_i & ~stall_dummy_instr & + !(instr_gets_expanded == INSTR_EXPANDED); end ////////// diff --git a/rtl/ibex_lockstep.sv b/rtl/ibex_lockstep.sv index cd016827bd..61dc450d6d 100644 --- a/rtl/ibex_lockstep.sv +++ b/rtl/ibex_lockstep.sv @@ -21,6 +21,7 @@ module ibex_lockstep import ibex_pkg::*; #( parameter bit RV32E = 1'b0, parameter rv32m_e RV32M = RV32MFast, parameter rv32b_e RV32B = RV32BNone, + parameter rv32zc_e RV32ZC = RV32ZcaZcbZcmp, parameter bit BranchTargetALU = 1'b0, parameter bit WritebackStage = 1'b0, parameter bit ICache = 1'b0, @@ -360,6 +361,7 @@ module ibex_lockstep import ibex_pkg::*; #( .RV32E ( RV32E ), .RV32M ( RV32M ), .RV32B ( RV32B ), + .RV32ZC ( RV32ZC ), .BranchTargetALU ( BranchTargetALU ), .ICache ( ICache ), .ICacheECC ( ICacheECC ), @@ -444,41 +446,44 @@ module ibex_lockstep import ibex_pkg::*; #( .double_fault_seen_o (shadow_outputs_d.double_fault_seen), `ifdef RVFI - .rvfi_valid (), - .rvfi_order (), - .rvfi_insn (), - .rvfi_trap (), - .rvfi_halt (), - .rvfi_intr (), - .rvfi_mode (), - .rvfi_ixl (), - .rvfi_rs1_addr (), - .rvfi_rs2_addr (), - .rvfi_rs3_addr (), - .rvfi_rs1_rdata (), - .rvfi_rs2_rdata (), - .rvfi_rs3_rdata (), - .rvfi_rd_addr (), - .rvfi_rd_wdata (), - .rvfi_pc_rdata (), - .rvfi_pc_wdata (), - .rvfi_mem_addr (), - .rvfi_mem_rmask (), - .rvfi_mem_wmask (), - .rvfi_mem_rdata (), - .rvfi_mem_wdata (), - .rvfi_ext_pre_mip (), - .rvfi_ext_post_mip (), - .rvfi_ext_nmi (), - .rvfi_ext_nmi_int (), - .rvfi_ext_debug_req (), - .rvfi_ext_debug_mode (), - .rvfi_ext_rf_wr_suppress (), - .rvfi_ext_mcycle (), - .rvfi_ext_mhpmcounters (), - .rvfi_ext_mhpmcountersh (), - .rvfi_ext_ic_scr_key_valid (), - .rvfi_ext_irq_valid (), + .rvfi_valid (), + .rvfi_order (), + .rvfi_insn (), + .rvfi_trap (), + .rvfi_halt (), + .rvfi_intr (), + .rvfi_mode (), + .rvfi_ixl (), + .rvfi_rs1_addr (), + .rvfi_rs2_addr (), + .rvfi_rs3_addr (), + .rvfi_rs1_rdata (), + .rvfi_rs2_rdata (), + .rvfi_rs3_rdata (), + .rvfi_rd_addr (), + .rvfi_rd_wdata (), + .rvfi_pc_rdata (), + .rvfi_pc_wdata (), + .rvfi_mem_addr (), + .rvfi_mem_rmask (), + .rvfi_mem_wmask (), + .rvfi_mem_rdata (), + .rvfi_mem_wdata (), + .rvfi_ext_pre_mip (), + .rvfi_ext_post_mip (), + .rvfi_ext_nmi (), + .rvfi_ext_nmi_int (), + .rvfi_ext_debug_req (), + .rvfi_ext_debug_mode (), + .rvfi_ext_rf_wr_suppress (), + .rvfi_ext_mcycle (), + .rvfi_ext_mhpmcounters (), + .rvfi_ext_mhpmcountersh (), + .rvfi_ext_ic_scr_key_valid (), + .rvfi_ext_irq_valid (), + .rvfi_ext_expanded_insn_valid (), + .rvfi_ext_expanded_insn (), + .rvfi_ext_expanded_insn_last (), `endif .fetch_enable_i (shadow_inputs_q[0].fetch_enable), diff --git a/rtl/ibex_pkg.sv b/rtl/ibex_pkg.sv index 265abff89b..58ecaea4b7 100644 --- a/rtl/ibex_pkg.sv +++ b/rtl/ibex_pkg.sv @@ -52,6 +52,13 @@ package ibex_pkg; RV32BFull = 3 } rv32b_e; + typedef enum integer { + RV32Zca = 0, + RV32ZcaZcb = 1, + RV32ZcaZcmp = 2, + RV32ZcaZcbZcmp = 3 + } rv32zc_e; + ///////////// // Opcodes // ///////////// @@ -300,6 +307,13 @@ package ibex_pkg; PC_BP } pc_sel_e; + // Compressed instruction expansion + typedef enum logic [1:0] { + INSTR_NOT_EXPANDED, + INSTR_EXPANDED, + INSTR_EXPANDED_LAST + } instr_exp_e; + // Exception PC mux selection typedef enum logic [1:0] { EXC_PC_EXC, diff --git a/rtl/ibex_top.sv b/rtl/ibex_top.sv index f846b13039..5233c6176a 100644 --- a/rtl/ibex_top.sv +++ b/rtl/ibex_top.sv @@ -24,6 +24,7 @@ module ibex_top import ibex_pkg::*; #( parameter bit RV32E = 1'b0, parameter rv32m_e RV32M = RV32MFast, parameter rv32b_e RV32B = RV32BNone, + parameter rv32zc_e RV32ZC = RV32ZcaZcbZcmp, parameter regfile_e RegFile = RegFileFF, parameter bit BranchTargetALU = 1'b0, parameter bit WritebackStage = 1'b0, @@ -148,6 +149,9 @@ module ibex_top import ibex_pkg::*; #( output logic [31:0] rvfi_ext_mhpmcountersh [10], output logic rvfi_ext_ic_scr_key_valid, output logic rvfi_ext_irq_valid, + output logic rvfi_ext_expanded_insn_valid, + output logic [15:0] rvfi_ext_expanded_insn, + output logic rvfi_ext_expanded_insn_last, `endif // CPU Control Signals @@ -310,6 +314,7 @@ module ibex_top import ibex_pkg::*; #( .RV32E (RV32E), .RV32M (RV32M), .RV32B (RV32B), + .RV32ZC (RV32ZC), .BranchTargetALU (BranchTargetALU), .ICache (ICache), .ICacheECC (ICacheECC), @@ -429,6 +434,9 @@ module ibex_top import ibex_pkg::*; #( .rvfi_ext_mhpmcountersh, .rvfi_ext_ic_scr_key_valid, .rvfi_ext_irq_valid, + .rvfi_ext_expanded_insn_valid, + .rvfi_ext_expanded_insn, + .rvfi_ext_expanded_insn_last, `endif .fetch_enable_i (fetch_enable_buf), @@ -1026,6 +1034,7 @@ module ibex_top import ibex_pkg::*; #( .RV32E (RV32E), .RV32M (RV32M), .RV32B (RV32B), + .RV32ZC (RV32ZC), .BranchTargetALU (BranchTargetALU), .ICache (ICache), .ICacheECC (ICacheECC), diff --git a/rtl/ibex_top_tracing.sv b/rtl/ibex_top_tracing.sv index 88586484d4..66e759763d 100644 --- a/rtl/ibex_top_tracing.sv +++ b/rtl/ibex_top_tracing.sv @@ -15,6 +15,7 @@ module ibex_top_tracing import ibex_pkg::*; #( parameter bit RV32E = 1'b0, parameter rv32m_e RV32M = RV32MFast, parameter rv32b_e RV32B = RV32BNone, + parameter rv32zc_e RV32ZC = RV32ZcaZcbZcmp, parameter regfile_e RegFile = RegFileFF, parameter bit BranchTargetALU = 1'b0, parameter bit WritebackStage = 1'b0, @@ -139,6 +140,9 @@ module ibex_top_tracing import ibex_pkg::*; #( logic [31:0] rvfi_ext_mhpmcountersh [10]; logic rvfi_ext_ic_scr_key_valid; logic rvfi_ext_irq_valid; + logic rvfi_ext_expanded_insn_valid; + logic [15:0] rvfi_ext_expanded_insn; + logic rvfi_ext_expanded_insn_last; logic [31:0] unused_perf_regs [10]; logic [31:0] unused_perf_regsh [10]; @@ -154,6 +158,7 @@ module ibex_top_tracing import ibex_pkg::*; #( logic [63:0] unused_rvfi_ext_mcycle; logic unused_rvfi_ext_ic_scr_key_valid; logic unused_rvfi_ext_irq_valid; + logic unused_rvfi_ext_expanded_insn_last; // Tracer doesn't use these signals, though other modules may probe down into tracer to observe // them. @@ -169,6 +174,7 @@ module ibex_top_tracing import ibex_pkg::*; #( assign unused_perf_regsh = rvfi_ext_mhpmcountersh; assign unused_rvfi_ext_ic_scr_key_valid = rvfi_ext_ic_scr_key_valid; assign unused_rvfi_ext_irq_valid = rvfi_ext_irq_valid; + assign unused_rvfi_ext_expanded_insn_last = rvfi_ext_expanded_insn_last; ibex_top #( .PMPEnable ( PMPEnable ), @@ -179,6 +185,7 @@ module ibex_top_tracing import ibex_pkg::*; #( .RV32E ( RV32E ), .RV32M ( RV32M ), .RV32B ( RV32B ), + .RV32ZC ( RV32ZC ), .RegFile ( RegFile ), .BranchTargetALU ( BranchTargetALU ), .ICache ( ICache ), @@ -279,6 +286,9 @@ module ibex_top_tracing import ibex_pkg::*; #( .rvfi_ext_mhpmcountersh, .rvfi_ext_ic_scr_key_valid, .rvfi_ext_irq_valid, + .rvfi_ext_expanded_insn_valid, + .rvfi_ext_expanded_insn, + .rvfi_ext_expanded_insn_last, .fetch_enable_i, .alert_minor_o, @@ -316,7 +326,9 @@ module ibex_top_tracing import ibex_pkg::*; #( .rvfi_mem_rmask, .rvfi_mem_wmask, .rvfi_mem_rdata, - .rvfi_mem_wdata + .rvfi_mem_wdata, + .rvfi_ext_expanded_insn_valid, + .rvfi_ext_expanded_insn ); endmodule diff --git a/rtl/ibex_tracer.sv b/rtl/ibex_tracer.sv index c086f52bac..789cb1ca8d 100644 --- a/rtl/ibex_tracer.sv +++ b/rtl/ibex_tracer.sv @@ -65,7 +65,9 @@ module ibex_tracer ( input logic [ 3:0] rvfi_mem_rmask, input logic [ 3:0] rvfi_mem_wmask, input logic [31:0] rvfi_mem_rdata, - input logic [31:0] rvfi_mem_wdata + input logic [31:0] rvfi_mem_wdata, + input logic rvfi_ext_expanded_insn_valid, + input logic [15:0] rvfi_ext_expanded_insn ); // These signals are part of RVFI, but not used in this module currently. @@ -143,6 +145,9 @@ module ibex_tracer ( $fwrite(fh, " load:0x%08x", rvfi_mem_rdata); end end + if (rvfi_ext_expanded_insn_valid) begin + $fwrite(fh, " expand_insn: (0x%04x %s)", rvfi_ext_expanded_insn, decode_expanded_insn()); + end $fwrite(fh, "\n"); endfunction @@ -157,6 +162,32 @@ module ibex_tracer ( end endfunction + // Format register address with "x" prefix, left-aligned to a fixed width of 3 characters. + function automatic string reg_addr_to_abi_str(input logic [4:0] addr); + case (addr) + // Hard-wired Zero Register + 5'd0: return "zero"; + // Return Address and Pointers + 5'd1: return "ra"; // Return Address + 5'd2: return "sp"; // Stack Pointer + 5'd3: return "gp"; // Global Pointer + 5'd4: return "tp"; // Thread Pointer + // Temporary Registers + 5'd5, 5'd6, 5'd7: return $sformatf("t%0d", addr - 5); + // Saved Registers + 5'd8, 5'd9: return $sformatf("s%0d", addr - 8); + // Function Arguments / Return Values + 5'd10, 5'd11, 5'd12, 5'd13, + 5'd14, 5'd15, 5'd16, 5'd17: return $sformatf("a%0d", addr - 10); + // Saved Registers + 5'd18, 5'd19, 5'd20, 5'd21, 5'd22, + 5'd23, 5'd24, 5'd25, 5'd26, 5'd27: return $sformatf("s%0d", addr - 16); + // Temporary Registers + 5'd28, 5'd29, 5'd30, 5'd31: return $sformatf("t%0d", addr - 25); + default: return $sformatf("x%0d", addr); + endcase + endfunction + // Get a CSR name for a CSR address. function automatic string get_csr_name(input logic [11:0] csr_addr); unique case (csr_addr) @@ -603,6 +634,11 @@ module ibex_tracer ( decoded_str = $sformatf("%s\t%0x", mnemonic, rvfi_pc_wdata); endfunction + function automatic void decode_Zc_cu_insn(input string mnemonic); + data_accessed = RS1 | RD; // RS1 == RD + decoded_str = $sformatf("%s\tx%0d", mnemonic, rvfi_rd_addr); + endfunction + function automatic void decode_compressed_load_insn(input string mnemonic); logic [7:0] imm; @@ -617,6 +653,20 @@ module ibex_tracer ( decoded_str = $sformatf("%s\tx%0d,%0d(x%0d)", mnemonic, rvfi_rd_addr, imm, rvfi_rs1_addr); endfunction + function automatic void decode_Zc_load_insn(input string mnemonic); + logic [1:0] imm; + + if (rvfi_insn[10] == 1'b0) begin + // C.LBU + imm = {rvfi_insn[5], rvfi_insn[6]}; + end else begin + // C.LHU, C.LH + imm = {rvfi_insn[5], 1'b0}; + end + data_accessed = RS1 | RD | MEM; + decoded_str = $sformatf("%s\tx%0d,%0d(x%0d)", mnemonic, rvfi_rd_addr, imm, rvfi_rs1_addr); + endfunction + function automatic void decode_compressed_store_insn(input string mnemonic); logic [7:0] imm; if (rvfi_insn[1:0] == OPCODE_C0) begin @@ -630,6 +680,69 @@ module ibex_tracer ( decoded_str = $sformatf("%s\tx%0d,%0d(x%0d)", mnemonic, rvfi_rs2_addr, imm, rvfi_rs1_addr); endfunction + function automatic void decode_Zc_store_insn(input string mnemonic); + logic [1:0] imm; + + if (rvfi_insn[10] == 1'b0) begin + // C.SB + imm = {rvfi_insn[5], rvfi_insn[6]}; + end else begin + // C.SH + imm = {rvfi_insn[5], 1'b0}; + end + data_accessed = RS1 | RS2 | MEM; + decoded_str = $sformatf("%s\tx%0d,%0d(x%0d)", mnemonic, rvfi_rd_addr, imm, rvfi_rs1_addr); + endfunction + + function automatic string cm_reg_to_str(input logic [2:0] addr); + logic [4:0] xreg = {addr[2:1] > 0, addr[2:1]==0, addr[2:0]}; + return reg_addr_to_abi_str(xreg); + endfunction + + function automatic string decode_Zcmp_cmmv_insn(input string mnemonic); + return $sformatf("%s\t%0s,%0s", mnemonic, cm_reg_to_str(rvfi_ext_expanded_insn[9:7]), + cm_reg_to_str(rvfi_ext_expanded_insn[4:2])); + endfunction + + function automatic string decode_Zcmp_cmpp_insn(input string mnemonic); + logic [3:0] rlist; + logic [1:0] spimm; + string rlist_str; + int base; + int spimm_val; + rlist = rvfi_ext_expanded_insn[7:4]; + spimm = rvfi_ext_expanded_insn[3:2]; + // Decode rlist to string + if (rlist < 4) begin + rlist_str = $sformatf("{INVALID (%0d)}", rlist); + end else begin + case(rlist) + 4: rlist_str = "{ra}"; + 5: rlist_str = "{ra, s0}"; + 15: rlist_str = "{ra, s0-s11}"; // The special case for s10/s11 + // The default case handles the general pattern for rlist 6 through 14 + default: rlist_str = $sformatf("{ra, s0-s%0d}", rlist - 5); + endcase + end + // Decode spimm + base = (rlist == 15) ? 64 : (32'(rlist) >> 2) * 16; + spimm_val = base + (spimm * 16); + spimm_val = mnemonic == "cm.push" ? -spimm_val : spimm_val; + return $sformatf("%s\t%s,%0d", mnemonic, rlist_str, spimm_val); + endfunction + + function automatic string decode_expanded_insn(); + unique casez (rvfi_ext_expanded_insn) + INSN_CMPUSH: return decode_Zcmp_cmpp_insn("cm.push"); + INSN_CMPOP: return decode_Zcmp_cmpp_insn("cm.pop"); + INSN_CMPOPRETZ: return decode_Zcmp_cmpp_insn("cm.popretz"); + INSN_CMPOPRET: return decode_Zcmp_cmpp_insn("cm.popret"); + INSN_CMMVSA01: return decode_Zcmp_cmmv_insn("cm.mvsa01"); + INSN_CMMVA01S: return decode_Zcmp_cmmv_insn("cm.mva01s"); + default: return "Decoding error"; + endcase + endfunction + function automatic void decode_load_insn(); string mnemonic; @@ -803,6 +916,13 @@ module ibex_tracer ( end INSN_CLW: decode_compressed_load_insn("c.lw"); INSN_CSW: decode_compressed_store_insn("c.sw"); + // Zc extension C0 + INSN_CLBU: decode_Zc_load_insn("c.lbu"); + INSN_CLHU: decode_Zc_load_insn("c.lhu"); + INSN_CLH: decode_Zc_load_insn("c.lh"); + INSN_CSB: decode_Zc_store_insn("c.sb"); + INSN_CSH: decode_Zc_store_insn("c.sh"); + // C1 Opcodes INSN_CADDI: decode_ci_caddi_insn("c.addi"); INSN_CJAL: decode_cj_insn("c.jal"); @@ -825,10 +945,21 @@ module ibex_tracer ( INSN_CAND: decode_cs_insn("c.and"); INSN_CBEQZ: decode_cb_insn("c.beqz"); INSN_CBNEZ: decode_cb_insn("c.bnez"); + // Zc extension C1 + INSN_CZEXTB: decode_Zc_cu_insn("c.zext.b"); + INSN_CSEXTB: decode_Zc_cu_insn("c.sext.b"); + INSN_CZEXTH: decode_Zc_cu_insn("c.zext.h"); + INSN_CSEXTH: decode_Zc_cu_insn("c.sext.h"); + INSN_CNOT: decode_Zc_cu_insn("c.not"); + INSN_CMUL: decode_cs_insn("c.mul"); + // C2 Opcodes INSN_CSLLI: decode_ci_cslli_insn("c.slli"); INSN_CLWSP: decode_compressed_load_insn("c.lwsp"); INSN_SWSP: decode_compressed_store_insn("c.swsp"); + // Zc extension C2: We should never see those on the rvfi_insn interface since they must + // get expanded into other instructions. They will be annotated through the + // `decode_expanded_insn` function instead and the `rvfi_ext_expanded_insn` signal. default: decode_mnemonic("INVALID"); endcase end diff --git a/rtl/ibex_tracer_pkg.sv b/rtl/ibex_tracer_pkg.sv index 6dbbfc9059..b1f975e8f4 100644 --- a/rtl/ibex_tracer_pkg.sv +++ b/rtl/ibex_tracer_pkg.sv @@ -305,6 +305,12 @@ package ibex_tracer_pkg; parameter logic [15:0] INSN_CADDI4SPN = { 3'b000, 11'h?, {OPCODE_C0} }; parameter logic [15:0] INSN_CLW = { 3'b010, 11'h?, {OPCODE_C0} }; parameter logic [15:0] INSN_CSW = { 3'b110, 11'h?, {OPCODE_C0} }; + // Zc extension C0 + parameter logic [15:0] INSN_CLBU = { 3'b100, 3'b000, 8'h?, {OPCODE_C0} }; + parameter logic [15:0] INSN_CLHU = { 3'b100, 3'b001, 3'h?, 1'b0, 4'h?, {OPCODE_C0} }; + parameter logic [15:0] INSN_CLH = { 3'b100, 3'b001, 3'h?, 1'b1, 4'h?, {OPCODE_C0} }; + parameter logic [15:0] INSN_CSB = { 3'b100, 3'b010, 8'h?, {OPCODE_C0} }; + parameter logic [15:0] INSN_CSH = { 3'b100, 3'b011, 3'h?, 1'b0, 4'h?, {OPCODE_C0} }; // C1 parameter logic [15:0] INSN_CADDI = { 3'b000, 11'h?, {OPCODE_C1} }; @@ -321,6 +327,13 @@ package ibex_tracer_pkg; parameter logic [15:0] INSN_CXOR = { 3'b100, 1'b0, 2'b11, 3'h?, 2'b01, 3'h?, {OPCODE_C1} }; parameter logic [15:0] INSN_COR = { 3'b100, 1'b0, 2'b11, 3'h?, 2'b10, 3'h?, {OPCODE_C1} }; parameter logic [15:0] INSN_CAND = { 3'b100, 1'b0, 2'b11, 3'h?, 2'b11, 3'h?, {OPCODE_C1} }; + // Zc extension C1 + parameter logic [15:0] INSN_CZEXTB = { 3'b100, 3'b111, 3'h?, 2'b11, 3'b000, {OPCODE_C1} }; + parameter logic [15:0] INSN_CSEXTB = { 3'b100, 3'b111, 3'h?, 2'b11, 3'b001, {OPCODE_C1} }; + parameter logic [15:0] INSN_CZEXTH = { 3'b100, 3'b111, 3'h?, 2'b11, 3'b010, {OPCODE_C1} }; + parameter logic [15:0] INSN_CSEXTH = { 3'b100, 3'b111, 3'h?, 2'b11, 3'b011, {OPCODE_C1} }; + parameter logic [15:0] INSN_CNOT = { 3'b100, 3'b111, 3'h?, 2'b11, 3'b101, {OPCODE_C1} }; + parameter logic [15:0] INSN_CMUL = { 3'b100, 3'b111, 3'h?, 2'b10, 3'h?, {OPCODE_C1} }; // C2 parameter logic [15:0] INSN_CSLLI = { 3'b000, 11'h?, {OPCODE_C2} }; @@ -331,5 +344,12 @@ package ibex_tracer_pkg; parameter logic [15:0] INSN_CEBREAK = { 3'b100, 1'b1, 5'h0, 5'h0, {OPCODE_C2} }; parameter logic [15:0] INSN_CJR = { 3'b100, 1'b0, 5'h0, 5'h0, {OPCODE_C2} }; parameter logic [15:0] INSN_CJALR = { 3'b100, 1'b1, 5'h?, 5'h0, {OPCODE_C2} }; + // Zc extension Cc + parameter logic [15:0] INSN_CMPUSH = { 3'b101, 3'b110, 2'b00, 4'h?, 2'b?, {OPCODE_C2} }; + parameter logic [15:0] INSN_CMPOP = { 3'b101, 3'b110, 2'b10, 4'h?, 2'b?, {OPCODE_C2} }; + parameter logic [15:0] INSN_CMPOPRETZ = { 3'b101, 3'b111, 2'b00, 4'h?, 2'b?, {OPCODE_C2} }; + parameter logic [15:0] INSN_CMPOPRET = { 3'b101, 3'b111, 2'b10, 4'h?, 2'b?, {OPCODE_C2} }; + parameter logic [15:0] INSN_CMMVSA01 = { 3'b101, 3'b011, 3'h?, 2'b01, 3'h?, {OPCODE_C2} }; + parameter logic [15:0] INSN_CMMVA01S = { 3'b101, 3'b011, 3'h?, 2'b11, 3'h?, {OPCODE_C2} }; endpackage