From db233f95dd694e25d71ac007ef14f3e97f7d7477 Mon Sep 17 00:00:00 2001 From: Elias Christen Date: Sun, 28 May 2023 21:53:02 +0200 Subject: [PATCH 01/16] [rtl] Implement the Zcb extension --- rtl/ibex_compressed_decoder.sv | 115 +++++++++++++++++++++++++++++++-- rtl/ibex_tracer.sv | 48 ++++++++++++++ rtl/ibex_tracer_pkg.sv | 13 ++++ 3 files changed, 170 insertions(+), 6 deletions(-) diff --git a/rtl/ibex_compressed_decoder.sv b/rtl/ibex_compressed_decoder.sv index e3c6aa52c7..3851935391 100644 --- a/rtl/ibex_compressed_decoder.sv +++ b/rtl/ibex_compressed_decoder.sv @@ -63,9 +63,64 @@ module ibex_compressed_decoder ( 2'b00, {OPCODE_STORE}}; end + 3'b100: begin // loads and stores + 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 + 3'b001, 3'b011, - 3'b100, 3'b101, 3'b111: begin illegal_instr_o = 1'b1; @@ -165,12 +220,60 @@ module ibex_compressed_decoder ( end 3'b100, - 3'b101, - 3'b110, + 3'b101: begin + // 100: c.subw + // 101: c.addw + illegal_instr_o = 1'b1; + end + + 3'b110: 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 + 3'b111: begin - // 100: c.subw - // 101: c.addw - illegal_instr_o = 1'b1; + 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 default: begin diff --git a/rtl/ibex_tracer.sv b/rtl/ibex_tracer.sv index c086f52bac..94a1f3758d 100644 --- a/rtl/ibex_tracer.sv +++ b/rtl/ibex_tracer.sv @@ -603,6 +603,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 +622,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 +649,20 @@ 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 void decode_load_insn(); string mnemonic; @@ -803,6 +836,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,6 +865,14 @@ 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"); diff --git a/rtl/ibex_tracer_pkg.sv b/rtl/ibex_tracer_pkg.sv index 6dbbfc9059..d0dea306a9 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} }; From 9406f5bbfd686fa543bbd8302acd3e2d1a4492e9 Mon Sep 17 00:00:00 2001 From: Andreas Kurth Date: Mon, 10 Jul 2023 09:53:59 +0000 Subject: [PATCH 02/16] [rtl] Implement Zcmp extension --- rtl/ibex_compressed_decoder.sv | 377 ++++++++++++++++++++++++++++++++- rtl/ibex_if_stage.sv | 11 +- 2 files changed, 382 insertions(+), 6 deletions(-) diff --git a/rtl/ibex_compressed_decoder.sv b/rtl/ibex_compressed_decoder.sv index 3851935391..b94a4e4b9d 100644 --- a/rtl/ibex_compressed_decoder.sv +++ b/rtl/ibex_compressed_decoder.sv @@ -17,9 +17,11 @@ module ibex_compressed_decoder ( 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 logic gets_expanded_o, output logic illegal_instr_o ); import ibex_pkg::*; @@ -29,14 +31,162 @@ module ibex_compressed_decoder ( logic unused_valid; assign unused_valid = valid_i; + 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 + + typedef enum logic [3:0] { + CmIdle, + CmPushStoreReg, + CmPushDecrSp, + CmPopLoadReg, + CmPopIncrSp, + CmPopZeroA0, + CmPopRetRa, + CmMvSA1, + CmMvA1S + } 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; + //////////////////////// // 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_o = 1'b0; + + // 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]) @@ -351,6 +501,218 @@ module ibex_compressed_decoder ( end end + 3'b101: begin + unique casez (instr_i[12:8]) + // cm.push + 5'b11000: begin + // This compressed instruction gets expanded into multiple instructions. + gets_expanded_o = 1'b1; + 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`. + 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 (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. + cm_sp_offset_d = 5'd2; + // Proceed with storing registers. + if (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_o = 1'b0; + 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_o = 1'b1; + 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`. + 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 (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 (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_o = 1'b0; + 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_o = 1'b0; + 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_o = 1'b1; + 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 (id_in_ready_i) begin + cm_state_d = CmMvSA1; + end + end + CmMvSA1: 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_o = 1'b0; + 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_o = 1'b1; + 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 (id_in_ready_i) begin + cm_state_d = CmMvA1S; + end + end + CmMvA1S: 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_o = 1'b0; + 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 + 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, @@ -359,7 +721,6 @@ module ibex_compressed_decoder ( 3'b001, 3'b011, - 3'b101, 3'b111: begin illegal_instr_o = 1'b1; end @@ -381,6 +742,18 @@ module ibex_compressed_decoder ( assign is_compressed_o = (instr_i[1:0] != 2'b11); + always_ff @(posedge clk_i, negedge rst_ni) begin + if (!rst_ni) begin + cm_state_q <= CmIdle; + // The following regs don't need to be reset as they get assigned before first usage: + // cm_rlist_q, cm_sp_offset_q + end else begin + cm_rlist_q <= cm_rlist_d; + cm_sp_offset_q <= cm_sp_offset_d; + cm_state_q <= cm_state_d; + end + end + //////////////// // Assertions // //////////////// diff --git a/rtl/ibex_if_stage.sv b/rtl/ibex_if_stage.sv index ebdd6e46c4..a3c9a7d5e5 100644 --- a/rtl/ibex_if_stage.sv +++ b/rtl/ibex_if_stage.sv @@ -144,6 +144,7 @@ module ibex_if_stage import ibex_pkg::*; #( logic [31:0] instr_decompressed; logic illegal_c_insn; logic instr_is_compressed; + logic instr_gets_expanded; logic if_instr_valid; logic [31:0] if_instr_rdata; @@ -405,9 +406,11 @@ module ibex_if_stage import ibex_pkg::*; #( .clk_i (clk_i), .rst_ni (rst_ni), .valid_i (fetch_valid & ~fetch_err), + .id_in_ready_i (id_in_ready_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) ); @@ -544,7 +547,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; always_ff @(posedge clk_i or negedge rst_ni) begin if (!rst_ni) begin @@ -606,7 +609,7 @@ 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) | + assign instr_skid_valid_d = (instr_skid_valid_q & ~id_in_ready_i & ~stall_dummy_instr & ~instr_gets_expanded) | instr_skid_en; always_ff @(posedge clk_i or negedge rst_ni) begin @@ -665,7 +668,7 @@ 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_skid_valid_q; assign instr_bp_taken_o = instr_bp_taken_q; @@ -680,7 +683,7 @@ 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; end ////////// From f9279291126d86838d3638d6386d60eb5908aaba Mon Sep 17 00:00:00 2001 From: Andreas Kurth Date: Mon, 10 Jul 2023 09:55:02 +0000 Subject: [PATCH 03/16] [rvfi] Output expanded instructions (except for the last one) --- rtl/ibex_core.sv | 4 +++- rtl/ibex_if_stage.sv | 6 ++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/rtl/ibex_core.sv b/rtl/ibex_core.sv index 56a59de5fd..4d107d8b39 100644 --- a/rtl/ibex_core.sv +++ b/rtl/ibex_core.sv @@ -184,6 +184,7 @@ 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; + logic instr_gets_expanded_id; logic instr_perf_count_id; logic instr_bp_taken_id; logic instr_fetch_err; // Bus error on instr fetch @@ -470,6 +471,7 @@ 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_bp_taken_o (instr_bp_taken_id), .instr_fetch_err_o (instr_fetch_err), .instr_fetch_err_plus2_o (instr_fetch_err_plus2), @@ -1766,7 +1768,7 @@ 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) begin rvfi_insn_id = {16'b0, instr_rdata_c_id}; end else begin rvfi_insn_id = instr_rdata_id; diff --git a/rtl/ibex_if_stage.sv b/rtl/ibex_if_stage.sv index a3c9a7d5e5..f48a5bdf6f 100644 --- a/rtl/ibex_if_stage.sv +++ b/rtl/ibex_if_stage.sv @@ -70,6 +70,7 @@ 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 logic instr_gets_expanded_id_o, output logic instr_bp_taken_o, // instruction was predicted to be // a taken branch output logic instr_fetch_err_o, // bus error on fetch @@ -162,6 +163,7 @@ module ibex_if_stage import ibex_pkg::*; #( logic stall_dummy_instr; logic [31:0] instr_out; logic instr_is_compressed_out; + logic instr_gets_expanded_out; logic illegal_c_instr_out; logic instr_err_out; @@ -439,6 +441,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 ? 1'b0 : 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; @@ -468,6 +471,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; @@ -517,6 +521,7 @@ 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; illegal_c_insn_id_o <= illegal_c_instr_out; pc_id_o <= pc_if_o; end @@ -531,6 +536,7 @@ 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; illegal_c_insn_id_o <= illegal_c_instr_out; pc_id_o <= pc_if_o; end From 7a89bc0fec1570367df9ecf7d98b1ecabb1a7c2e Mon Sep 17 00:00:00 2001 From: Samuel Riedel Date: Thu, 3 Jul 2025 09:56:43 +0000 Subject: [PATCH 04/16] [rtl] Parametrize Zcb and Zcmp extensions --- rtl/ibex_compressed_decoder.sv | 574 +++++++++++++++++---------------- rtl/ibex_core.sv | 2 + rtl/ibex_if_stage.sv | 5 +- rtl/ibex_lockstep.sv | 2 + rtl/ibex_pkg.sv | 7 + rtl/ibex_top.sv | 3 + rtl/ibex_top_tracing.sv | 2 + 7 files changed, 317 insertions(+), 278 deletions(-) diff --git a/rtl/ibex_compressed_decoder.sv b/rtl/ibex_compressed_decoder.sv index b94a4e4b9d..082589130a 100644 --- a/rtl/ibex_compressed_decoder.sv +++ b/rtl/ibex_compressed_decoder.sv @@ -13,7 +13,9 @@ `include "prim_assert.sv" -module ibex_compressed_decoder ( +module ibex_compressed_decoder #( + parameter ibex_pkg::rv32zc_e RV32ZC = ibex_pkg::RV32ZcaZcbZcmp +) ( input logic clk_i, input logic rst_ni, input logic valid_i, @@ -214,59 +216,65 @@ module ibex_compressed_decoder ( end 3'b100: begin // loads and stores - 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') + 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'b001, 2'b01, instr_i[4:2], {OPCODE_LOAD}}; - end + 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 - 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 + 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 + 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, @@ -371,59 +379,68 @@ module ibex_compressed_decoder ( 3'b100, 3'b101: begin - // 100: c.subw - // 101: c.addw - illegal_instr_o = 1'b1; + // 100: c.subw + // 101: c.addw + illegal_instr_o = 1'b1; end 3'b110: 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}}; + 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 - 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 + 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'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'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'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'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 + 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 + 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 @@ -502,215 +519,218 @@ module ibex_compressed_decoder ( end 3'b101: begin - unique casez (instr_i[12:8]) - // cm.push - 5'b11000: begin - // This compressed instruction gets expanded into multiple instructions. - gets_expanded_o = 1'b1; - 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`. - 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 (id_in_ready_i) begin - cm_state_d = CmPushDecrSp; + 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_o = 1'b1; + 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`. + 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 (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. + cm_sp_offset_d = 5'd2; + // Proceed with storing registers. + if (id_in_ready_i) begin + cm_state_d = CmPushStoreReg; + end 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. - cm_sp_offset_d = 5'd2; - // Proceed with storing registers. + 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 - cm_state_d = CmPushStoreReg; + // 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 - 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; + 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_o = 1'b0; + cm_state_d = CmIdle; 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_o = 1'b0; - cm_state_d = CmIdle; - end - end - default: cm_state_d = CmIdle; - endcase - 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_o = 1'b1; - 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`. - 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 (id_in_ready_i) begin - cm_state_d = CmPopIncrSp; + // cm.pop, cm.popretz, cm.popret + 5'b11010, + 5'b11100, + 5'b11110: begin + // This compressed instruction gets expanded into multiple instructions. + gets_expanded_o = 1'b1; + 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`. + 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 (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 (id_in_ready_i) begin + cm_state_d = CmPopLoadReg; + end 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. + 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 - cm_state_d = CmPopLoadReg; + // 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 - 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; + 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_o = 1'b0; + cm_state_d = CmIdle; + end + endcase 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_o = 1'b0; - 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; + CmPopZeroA0: begin + instr_o = cm_zero_a0(); + if (id_in_ready_i) begin + cm_state_d = CmPopRetRa; + end 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_o = 1'b0; - cm_state_d = CmIdle; + 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_o = 1'b0; + cm_state_d = CmIdle; + end end - end - default: cm_state_d = CmIdle; - endcase - 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_o = 1'b1; - 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 (id_in_ready_i) begin - cm_state_d = CmMvSA1; + // 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_o = 1'b1; + 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 (id_in_ready_i) begin + cm_state_d = CmMvSA1; + end end - end - CmMvSA1: 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_o = 1'b0; - cm_state_d = CmIdle; + CmMvSA1: 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_o = 1'b0; + cm_state_d = CmIdle; + end end - end - default: cm_state_d = CmIdle; - endcase - end + default: cm_state_d = CmIdle; + endcase + end - // cm.mva01s - 2'b11: begin - // This compressed instruction gets expanded into multiple instructions. - gets_expanded_o = 1'b1; - 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 (id_in_ready_i) begin - cm_state_d = CmMvA1S; + // cm.mva01s + 2'b11: begin + // This compressed instruction gets expanded into multiple instructions. + gets_expanded_o = 1'b1; + 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 (id_in_ready_i) begin + cm_state_d = CmMvA1S; + end end - end - CmMvA1S: 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_o = 1'b0; - cm_state_d = CmIdle; + CmMvA1S: 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_o = 1'b0; + cm_state_d = CmIdle; + end end - end - default: cm_state_d = CmIdle; - endcase - end - default: illegal_instr_o = 1'b1; - endcase - end + default: cm_state_d = CmIdle; + endcase + end + default: illegal_instr_o = 1'b1; + endcase + end - default: illegal_instr_o = 1'b1; - endcase + 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 diff --git a/rtl/ibex_core.sv b/rtl/ibex_core.sv index 4d107d8b39..92a3d04e62 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, @@ -424,6 +425,7 @@ module ibex_core import ibex_pkg::*; #( .DmExceptionAddr (DmExceptionAddr), .DummyInstructions(DummyInstructions), .ICache (ICache), + .RV32ZC (RV32ZC), .ICacheECC (ICacheECC), .BusSizeECC (BusSizeECC), .TagSizeECC (TagSizeECC), diff --git a/rtl/ibex_if_stage.sv b/rtl/ibex_if_stage.sv index f48a5bdf6f..4e86b005d9 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, @@ -404,7 +405,9 @@ 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) + ) compressed_decoder_i ( .clk_i (clk_i), .rst_ni (rst_ni), .valid_i (fetch_valid & ~fetch_err), diff --git a/rtl/ibex_lockstep.sv b/rtl/ibex_lockstep.sv index cd016827bd..e33b0fbbad 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 ), diff --git a/rtl/ibex_pkg.sv b/rtl/ibex_pkg.sv index 265abff89b..7d584dd4a8 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 // ///////////// diff --git a/rtl/ibex_top.sv b/rtl/ibex_top.sv index f846b13039..6d2ef50b73 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, @@ -310,6 +311,7 @@ module ibex_top import ibex_pkg::*; #( .RV32E (RV32E), .RV32M (RV32M), .RV32B (RV32B), + .RV32ZC (RV32ZC), .BranchTargetALU (BranchTargetALU), .ICache (ICache), .ICacheECC (ICacheECC), @@ -1026,6 +1028,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..50c99f217f 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, @@ -179,6 +180,7 @@ module ibex_top_tracing import ibex_pkg::*; #( .RV32E ( RV32E ), .RV32M ( RV32M ), .RV32B ( RV32B ), + .RV32ZC ( RV32ZC ), .RegFile ( RegFile ), .BranchTargetALU ( BranchTargetALU ), .ICache ( ICache ), From 2e41491e43ac8a17e7718cebf8c9e975cdf0ea0d Mon Sep 17 00:00:00 2001 From: Samuel Riedel Date: Wed, 27 Aug 2025 08:54:47 +0000 Subject: [PATCH 05/16] [doc] Add Zcb and Zcmp extension to documentation --- doc/01_overview/compliance.rst | 8 ++ doc/02_user/integration.rst | 139 ++++++++++++++------------ doc/03_reference/pipeline_details.rst | 10 ++ 3 files changed, 91 insertions(+), 66 deletions(-) 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. | ++-----------------------+--------------------------------------+-------------------------------------------------------------+ From e8c7e6de7471746b27cf6a67e85a501354d5ab44 Mon Sep 17 00:00:00 2001 From: Samuel Riedel Date: Tue, 9 Sep 2025 14:19:07 +0000 Subject: [PATCH 06/16] [rtl] Track last expanded instruction This is required to ensure we can trace all expanded instructions but also already advance the PC on the last expanded instruction --- rtl/ibex_compressed_decoder.sv | 38 +++++++++++++++++----------------- rtl/ibex_core.sv | 4 ++-- rtl/ibex_if_stage.sv | 20 ++++++++++-------- rtl/ibex_pkg.sv | 7 +++++++ 4 files changed, 39 insertions(+), 30 deletions(-) diff --git a/rtl/ibex_compressed_decoder.sv b/rtl/ibex_compressed_decoder.sv index 082589130a..114c30157b 100644 --- a/rtl/ibex_compressed_decoder.sv +++ b/rtl/ibex_compressed_decoder.sv @@ -16,15 +16,15 @@ module ibex_compressed_decoder #( parameter ibex_pkg::rv32zc_e RV32ZC = ibex_pkg::RV32ZcaZcbZcmp ) ( - 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 logic gets_expanded_o, - output logic illegal_instr_o + 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::*; @@ -183,7 +183,7 @@ module ibex_compressed_decoder #( // By default, forward incoming instruction, mark it as legal, and don't expand. instr_o = instr_i; illegal_instr_o = 1'b0; - gets_expanded_o = 1'b0; + gets_expanded_o = INSTR_NOT_EXPANDED; // Maintain state of CM FSM. cm_rlist_d = cm_rlist_q; @@ -524,7 +524,7 @@ module ibex_compressed_decoder #( // cm.push 5'b11000: begin // This compressed instruction gets expanded into multiple instructions. - gets_expanded_o = 1'b1; + gets_expanded_o = INSTR_EXPANDED; unique case (cm_state_q) CmIdle: begin // No cm.push instruction is active yet; start a new one. @@ -573,7 +573,7 @@ module ibex_compressed_decoder #( 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_o = 1'b0; + gets_expanded_o = INSTR_EXPANDED_LAST; cm_state_d = CmIdle; end end @@ -586,7 +586,7 @@ module ibex_compressed_decoder #( 5'b11100, 5'b11110: begin // This compressed instruction gets expanded into multiple instructions. - gets_expanded_o = 1'b1; + gets_expanded_o = INSTR_EXPANDED; unique case (cm_state_q) CmIdle: begin // No cm.pop instruction is active yet; start a new one. @@ -643,7 +643,7 @@ module ibex_compressed_decoder #( 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_o = 1'b0; + gets_expanded_o = INSTR_EXPANDED_LAST; cm_state_d = CmIdle; end endcase @@ -659,7 +659,7 @@ module ibex_compressed_decoder #( 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_o = 1'b0; + gets_expanded_o = INSTR_EXPANDED_LAST; cm_state_d = CmIdle; end end @@ -673,7 +673,7 @@ module ibex_compressed_decoder #( // cm.mvsa01 2'b01: begin // This compressed instruction gets expanded into multiple instructions. - gets_expanded_o = 1'b1; + gets_expanded_o = INSTR_EXPANDED; unique case (cm_state_q) CmIdle: begin // No cm.mvsa01 instruction is active yet; start a new one. @@ -688,7 +688,7 @@ module ibex_compressed_decoder #( 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_o = 1'b0; + gets_expanded_o = INSTR_EXPANDED_LAST; cm_state_d = CmIdle; end end @@ -699,7 +699,7 @@ module ibex_compressed_decoder #( // cm.mva01s 2'b11: begin // This compressed instruction gets expanded into multiple instructions. - gets_expanded_o = 1'b1; + gets_expanded_o = INSTR_EXPANDED; unique case (cm_state_q) CmIdle: begin // No cm.mva01s instruction is active yet; start a new one. @@ -714,7 +714,7 @@ module ibex_compressed_decoder #( 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_o = 1'b0; + gets_expanded_o = INSTR_EXPANDED_LAST; cm_state_d = CmIdle; end end diff --git a/rtl/ibex_core.sv b/rtl/ibex_core.sv index 92a3d04e62..b208e29cc3 100644 --- a/rtl/ibex_core.sv +++ b/rtl/ibex_core.sv @@ -185,7 +185,7 @@ 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; - logic instr_gets_expanded_id; + instr_exp_e instr_gets_expanded_id; logic instr_perf_count_id; logic instr_bp_taken_id; logic instr_fetch_err; // Bus error on instr fetch @@ -1770,7 +1770,7 @@ module ibex_core import ibex_pkg::*; #( end always_comb begin - if (instr_is_compressed_id && !instr_gets_expanded_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; diff --git a/rtl/ibex_if_stage.sv b/rtl/ibex_if_stage.sv index 4e86b005d9..cc9dd8792c 100644 --- a/rtl/ibex_if_stage.sv +++ b/rtl/ibex_if_stage.sv @@ -71,7 +71,7 @@ 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 logic instr_gets_expanded_id_o, + output instr_exp_e instr_gets_expanded_id_o, output logic instr_bp_taken_o, // instruction was predicted to be // a taken branch output logic instr_fetch_err_o, // bus error on fetch @@ -146,7 +146,7 @@ module ibex_if_stage import ibex_pkg::*; #( logic [31:0] instr_decompressed; logic illegal_c_insn; logic instr_is_compressed; - logic instr_gets_expanded; + instr_exp_e instr_gets_expanded; logic if_instr_valid; logic [31:0] if_instr_rdata; @@ -164,7 +164,7 @@ module ibex_if_stage import ibex_pkg::*; #( logic stall_dummy_instr; logic [31:0] instr_out; logic instr_is_compressed_out; - logic instr_gets_expanded_out; + instr_exp_e instr_gets_expanded_out; logic illegal_c_instr_out; logic instr_err_out; @@ -444,7 +444,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 ? 1'b0 : instr_gets_expanded; + 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; @@ -556,7 +556,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 & ~instr_gets_expanded; + ~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 @@ -618,8 +618,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_gets_expanded) | - 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 @@ -677,7 +677,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_gets_expanded & ~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; @@ -692,7 +693,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 & ~instr_gets_expanded; + assign fetch_ready = id_in_ready_i & ~stall_dummy_instr & + !(instr_gets_expanded == INSTR_EXPANDED); end ////////// diff --git a/rtl/ibex_pkg.sv b/rtl/ibex_pkg.sv index 7d584dd4a8..58ecaea4b7 100644 --- a/rtl/ibex_pkg.sv +++ b/rtl/ibex_pkg.sv @@ -307,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, From 9a09039026fa5fe64e6cf4358096cb6c24242964 Mon Sep 17 00:00:00 2001 From: Samuel Riedel Date: Thu, 18 Sep 2025 12:24:04 +0000 Subject: [PATCH 07/16] [rvfi] Track expanded instruction in rvfi Tracking also the original instruction, not only the micro-op, allows the DV to track whether and which instruction we are expanding --- rtl/ibex_core.sv | 279 +++++++++++++++++++++++----------------- rtl/ibex_if_stage.sv | 10 +- rtl/ibex_lockstep.sv | 73 ++++++----- rtl/ibex_top.sv | 6 + rtl/ibex_top_tracing.sv | 12 ++ 5 files changed, 223 insertions(+), 157 deletions(-) diff --git a/rtl/ibex_core.sv b/rtl/ibex_core.sv index b208e29cc3..f4b9d4c3cf 100644 --- a/rtl/ibex_core.sv +++ b/rtl/ibex_core.sv @@ -159,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 @@ -186,6 +189,7 @@ module ibex_core import ibex_pkg::*; #( 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 @@ -474,6 +478,7 @@ module ibex_core import ibex_pkg::*; #( .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), @@ -1300,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]; @@ -1363,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 @@ -1558,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 @@ -1704,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 @@ -1777,6 +1801,19 @@ module ibex_core import ibex_pkg::*; #( 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 cc9dd8792c..37f52ed250 100644 --- a/rtl/ibex_if_stage.sv +++ b/rtl/ibex_if_stage.sv @@ -71,7 +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, + 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 @@ -514,6 +518,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 @@ -525,6 +531,7 @@ module ibex_if_stage import ibex_pkg::*; #( 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 @@ -540,6 +547,7 @@ module ibex_if_stage import ibex_pkg::*; #( 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 diff --git a/rtl/ibex_lockstep.sv b/rtl/ibex_lockstep.sv index e33b0fbbad..61dc450d6d 100644 --- a/rtl/ibex_lockstep.sv +++ b/rtl/ibex_lockstep.sv @@ -446,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_top.sv b/rtl/ibex_top.sv index 6d2ef50b73..5233c6176a 100644 --- a/rtl/ibex_top.sv +++ b/rtl/ibex_top.sv @@ -149,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 @@ -431,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), diff --git a/rtl/ibex_top_tracing.sv b/rtl/ibex_top_tracing.sv index 50c99f217f..1310d81fc1 100644 --- a/rtl/ibex_top_tracing.sv +++ b/rtl/ibex_top_tracing.sv @@ -140,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]; @@ -155,6 +158,9 @@ 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_valid; + logic [15:0] unused_rvfi_ext_expanded_insn; + logic unused_rvfi_ext_expanded_insn_last; // Tracer doesn't use these signals, though other modules may probe down into tracer to observe // them. @@ -170,6 +176,9 @@ 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_valid = rvfi_ext_expanded_insn_valid; + assign unused_rvfi_ext_expanded_insn = rvfi_ext_expanded_insn; + assign unused_rvfi_ext_expanded_insn_last = rvfi_ext_expanded_insn_last; ibex_top #( .PMPEnable ( PMPEnable ), @@ -281,6 +290,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, From 4530af8e05d3a5d3a6cffc87fa47ea623f7d31fb Mon Sep 17 00:00:00 2001 From: Samuel Riedel Date: Tue, 7 Oct 2025 17:00:56 +0000 Subject: [PATCH 08/16] [rtl] Annotate traces with expanded instruction information The tracer usually only sees the instructions that reach the ID stage. Since the Zcmp instructions are expanded in the IF stage, they will be traced as their micro-ops. This adds information in the trace from which expanded instruction those micro-ops come from. --- rtl/ibex_top_tracing.sv | 8 ++-- rtl/ibex_tracer.sv | 85 ++++++++++++++++++++++++++++++++++++++++- rtl/ibex_tracer_pkg.sv | 7 ++++ 3 files changed, 94 insertions(+), 6 deletions(-) diff --git a/rtl/ibex_top_tracing.sv b/rtl/ibex_top_tracing.sv index 1310d81fc1..66e759763d 100644 --- a/rtl/ibex_top_tracing.sv +++ b/rtl/ibex_top_tracing.sv @@ -158,8 +158,6 @@ 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_valid; - logic [15:0] unused_rvfi_ext_expanded_insn; logic unused_rvfi_ext_expanded_insn_last; // Tracer doesn't use these signals, though other modules may probe down into tracer to observe @@ -176,8 +174,6 @@ 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_valid = rvfi_ext_expanded_insn_valid; - assign unused_rvfi_ext_expanded_insn = rvfi_ext_expanded_insn; assign unused_rvfi_ext_expanded_insn_last = rvfi_ext_expanded_insn_last; ibex_top #( @@ -330,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 94a1f3758d..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) @@ -663,6 +694,55 @@ module ibex_tracer ( 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; @@ -877,6 +957,9 @@ module ibex_tracer ( 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 d0dea306a9..b1f975e8f4 100644 --- a/rtl/ibex_tracer_pkg.sv +++ b/rtl/ibex_tracer_pkg.sv @@ -344,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 From 64e3f79ca8ec7d0c1fb54e0e35416737e925dc66 Mon Sep 17 00:00:00 2001 From: Samuel Riedel Date: Thu, 9 Oct 2025 23:57:28 +0000 Subject: [PATCH 09/16] [rtl] Fix handshake on compressed decoder The handshake only considered whether the ID stage would be ready. But the actual pipeline register will also take the `pc_set_i` signal into account, which signals a jump. Since the compressed decoder has state now (through the Zcmp extension), this improper handshake led to some of the expanded instructions to get lost. At the same time, we also take this signal into account for the enable signal of the pipeline stage to avoid unnecessary switching. --- rtl/ibex_if_stage.sv | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rtl/ibex_if_stage.sv b/rtl/ibex_if_stage.sv index 37f52ed250..89faea3a52 100644 --- a/rtl/ibex_if_stage.sv +++ b/rtl/ibex_if_stage.sv @@ -415,7 +415,7 @@ module ibex_if_stage import ibex_pkg::*; #( .clk_i (clk_i), .rst_ni (rst_ni), .valid_i (fetch_valid & ~fetch_err), - .id_in_ready_i (id_in_ready_i), + .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), @@ -490,7 +490,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 From 67432d609ebd67ae7ecc1f9bf5f217bc96cd987b Mon Sep 17 00:00:00 2001 From: Samuel Riedel Date: Fri, 31 Oct 2025 16:47:46 +0000 Subject: [PATCH 10/16] [rtl] Add explanations to the Zcmp state machine --- rtl/ibex_compressed_decoder.sv | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/rtl/ibex_compressed_decoder.sv b/rtl/ibex_compressed_decoder.sv index 114c30157b..7771befc61 100644 --- a/rtl/ibex_compressed_decoder.sv +++ b/rtl/ibex_compressed_decoder.sv @@ -160,15 +160,22 @@ module ibex_compressed_decoder #( return rlist; endfunction + // Combined FSM state register for Zcmp operations. + // This single 4-bit enum represents 4 independent "virtual" FSMs + // that share the CmIdle state. typedef enum logic [3:0] { CmIdle, + // cm.push CmPushStoreReg, CmPushDecrSp, + // cm.pop, cm.popret, cm.popretz CmPopLoadReg, CmPopIncrSp, CmPopZeroA0, CmPopRetRa, + // cm.mvsa01 CmMvSA1, + // cm.mva01s CmMvA1S } cm_state_e; logic [4:0] cm_rlist_d, cm_rlist_q; @@ -530,7 +537,8 @@ module ibex_compressed_decoder #( // 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`. + // 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. @@ -546,6 +554,8 @@ module ibex_compressed_decoder #( // 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 (id_in_ready_i) begin @@ -595,7 +605,8 @@ module ibex_compressed_decoder #( // 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`. + // 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. From 8328363a21872bbac0023895c7972b18e70ca208 Mon Sep 17 00:00:00 2001 From: Samuel Riedel Date: Fri, 31 Oct 2025 16:52:25 +0000 Subject: [PATCH 11/16] [rtl] Optimize the Zcmp FSM Merge the two cm.mv* states into a single one. This should still be easy to understand and saves us an extra bit in the encoding. --- rtl/ibex_compressed_decoder.sv | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/rtl/ibex_compressed_decoder.sv b/rtl/ibex_compressed_decoder.sv index 7771befc61..fec87b5d8f 100644 --- a/rtl/ibex_compressed_decoder.sv +++ b/rtl/ibex_compressed_decoder.sv @@ -161,9 +161,9 @@ module ibex_compressed_decoder #( endfunction // Combined FSM state register for Zcmp operations. - // This single 4-bit enum represents 4 independent "virtual" FSMs - // that share the CmIdle state. - typedef enum logic [3:0] { + // 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, @@ -173,10 +173,8 @@ module ibex_compressed_decoder #( CmPopIncrSp, CmPopZeroA0, CmPopRetRa, - // cm.mvsa01 - CmMvSA1, - // cm.mva01s - CmMvA1S + // 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; @@ -691,10 +689,10 @@ module ibex_compressed_decoder #( // Move a0 to register indicated by r1s'. instr_o = cm_mvsa01(.a01(1'b0), .rs(instr_i[9:7])); if (id_in_ready_i) begin - cm_state_d = CmMvSA1; + cm_state_d = CmMvSecondReg; end end - CmMvSA1: begin + 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 @@ -717,10 +715,10 @@ module ibex_compressed_decoder #( // Move register indicated by r1s' into a0. instr_o = cm_mva01s(.rs(instr_i[9:7]), .a01(1'b0)); if (id_in_ready_i) begin - cm_state_d = CmMvA1S; + cm_state_d = CmMvSecondReg; end end - CmMvA1S: begin + 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 From 50634fdc76208fee7aa3dd152041f54d6a5a5f82 Mon Sep 17 00:00:00 2001 From: Samuel Riedel Date: Mon, 15 Dec 2025 15:42:07 +0000 Subject: [PATCH 12/16] [rtl] Only start the push/pop FSM if the instruction is valid --- rtl/ibex_compressed_decoder.sv | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/rtl/ibex_compressed_decoder.sv b/rtl/ibex_compressed_decoder.sv index fec87b5d8f..ab2abe4fc6 100644 --- a/rtl/ibex_compressed_decoder.sv +++ b/rtl/ibex_compressed_decoder.sv @@ -28,10 +28,15 @@ module ibex_compressed_decoder #( ); 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) @@ -544,7 +549,7 @@ module ibex_compressed_decoder #( 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 (id_in_ready_i) begin + if (valid_i && id_in_ready_i) begin cm_state_d = CmPushDecrSp; end end else begin @@ -556,7 +561,7 @@ module ibex_compressed_decoder #( // offset from 2 onwards in CmPushStoreReg next cycle. cm_sp_offset_d = 5'd2; // Proceed with storing registers. - if (id_in_ready_i) begin + if (valid_i && id_in_ready_i) begin cm_state_d = CmPushStoreReg; end end @@ -612,7 +617,7 @@ module ibex_compressed_decoder #( 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 (id_in_ready_i) begin + if (valid_i && id_in_ready_i) begin cm_state_d = CmPopIncrSp; end end else begin @@ -621,7 +626,7 @@ module ibex_compressed_decoder #( cm_rlist_d -= 5'd1; cm_sp_offset_d -= 5'd1; // Proceed with loading registers. - if (id_in_ready_i) begin + if (valid_i && id_in_ready_i) begin cm_state_d = CmPopLoadReg; end end @@ -688,7 +693,7 @@ module ibex_compressed_decoder #( // 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 (id_in_ready_i) begin + if (valid_i && id_in_ready_i) begin cm_state_d = CmMvSecondReg; end end @@ -714,7 +719,7 @@ module ibex_compressed_decoder #( // 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 (id_in_ready_i) begin + if (valid_i && id_in_ready_i) begin cm_state_d = CmMvSecondReg; end end From 900ea8c01be66fc06b429f57752cdb733aa0f0bf Mon Sep 17 00:00:00 2001 From: Samuel Riedel Date: Fri, 5 Dec 2025 13:07:39 +0000 Subject: [PATCH 13/16] [rtl] Add assertion to ensure the push/pop FSM only advances if valid --- rtl/ibex_compressed_decoder.sv | 1 + 1 file changed, 1 insertion(+) diff --git a/rtl/ibex_compressed_decoder.sv b/rtl/ibex_compressed_decoder.sv index ab2abe4fc6..6055cca7d0 100644 --- a/rtl/ibex_compressed_decoder.sv +++ b/rtl/ibex_compressed_decoder.sv @@ -809,5 +809,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 From c9fb2ceb3652bfc2869dd8b8ca69ca8074e4b540 Mon Sep 17 00:00:00 2001 From: Samuel Riedel Date: Thu, 18 Dec 2025 01:36:23 +0000 Subject: [PATCH 14/16] [rtl] Compute `gets_expanded_o` only if the instruction is valid --- rtl/ibex_compressed_decoder.sv | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/rtl/ibex_compressed_decoder.sv b/rtl/ibex_compressed_decoder.sv index 6055cca7d0..cd7090688c 100644 --- a/rtl/ibex_compressed_decoder.sv +++ b/rtl/ibex_compressed_decoder.sv @@ -185,6 +185,17 @@ module ibex_compressed_decoder #( 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 // //////////////////////// @@ -193,7 +204,7 @@ module ibex_compressed_decoder #( // By default, forward incoming instruction, mark it as legal, and don't expand. instr_o = instr_i; illegal_instr_o = 1'b0; - gets_expanded_o = INSTR_NOT_EXPANDED; + gets_expanded = INSTR_NOT_EXPANDED; // Maintain state of CM FSM. cm_rlist_d = cm_rlist_q; @@ -534,7 +545,7 @@ module ibex_compressed_decoder #( // cm.push 5'b11000: begin // This compressed instruction gets expanded into multiple instructions. - gets_expanded_o = INSTR_EXPANDED; + gets_expanded = INSTR_EXPANDED; unique case (cm_state_q) CmIdle: begin // No cm.push instruction is active yet; start a new one. @@ -586,7 +597,7 @@ module ibex_compressed_decoder #( 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_o = INSTR_EXPANDED_LAST; + gets_expanded = INSTR_EXPANDED_LAST; cm_state_d = CmIdle; end end @@ -599,7 +610,7 @@ module ibex_compressed_decoder #( 5'b11100, 5'b11110: begin // This compressed instruction gets expanded into multiple instructions. - gets_expanded_o = INSTR_EXPANDED; + gets_expanded = INSTR_EXPANDED; unique case (cm_state_q) CmIdle: begin // No cm.pop instruction is active yet; start a new one. @@ -657,7 +668,7 @@ module ibex_compressed_decoder #( 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_o = INSTR_EXPANDED_LAST; + gets_expanded = INSTR_EXPANDED_LAST; cm_state_d = CmIdle; end endcase @@ -673,7 +684,7 @@ module ibex_compressed_decoder #( 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_o = INSTR_EXPANDED_LAST; + gets_expanded = INSTR_EXPANDED_LAST; cm_state_d = CmIdle; end end @@ -687,7 +698,7 @@ module ibex_compressed_decoder #( // cm.mvsa01 2'b01: begin // This compressed instruction gets expanded into multiple instructions. - gets_expanded_o = INSTR_EXPANDED; + gets_expanded = INSTR_EXPANDED; unique case (cm_state_q) CmIdle: begin // No cm.mvsa01 instruction is active yet; start a new one. @@ -702,7 +713,7 @@ module ibex_compressed_decoder #( 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_o = INSTR_EXPANDED_LAST; + gets_expanded = INSTR_EXPANDED_LAST; cm_state_d = CmIdle; end end @@ -713,7 +724,7 @@ module ibex_compressed_decoder #( // cm.mva01s 2'b11: begin // This compressed instruction gets expanded into multiple instructions. - gets_expanded_o = INSTR_EXPANDED; + gets_expanded = INSTR_EXPANDED; unique case (cm_state_q) CmIdle: begin // No cm.mva01s instruction is active yet; start a new one. @@ -728,7 +739,7 @@ module ibex_compressed_decoder #( 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_o = INSTR_EXPANDED_LAST; + gets_expanded = INSTR_EXPANDED_LAST; cm_state_d = CmIdle; end end From 3074de4865eb55430d7bedfc96abeb2be2874c0b Mon Sep 17 00:00:00 2001 From: Samuel Riedel Date: Mon, 22 Dec 2025 22:29:58 +0000 Subject: [PATCH 15/16] [rtl] Make Zcmp state registers resettable --- rtl/ibex_compressed_decoder.sv | 25 ++++++++++++++++++++----- rtl/ibex_if_stage.sv | 3 ++- 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/rtl/ibex_compressed_decoder.sv b/rtl/ibex_compressed_decoder.sv index cd7090688c..0adf3f0f0d 100644 --- a/rtl/ibex_compressed_decoder.sv +++ b/rtl/ibex_compressed_decoder.sv @@ -14,7 +14,8 @@ `include "prim_assert.sv" module ibex_compressed_decoder #( - parameter ibex_pkg::rv32zc_e RV32ZC = ibex_pkg::RV32ZcaZcbZcmp + parameter ibex_pkg::rv32zc_e RV32ZC = ibex_pkg::RV32ZcaZcbZcmp, + parameter bit ResetAll = 1'b0 ) ( input logic clk_i, input logic rst_ni, @@ -787,15 +788,29 @@ module ibex_compressed_decoder #( assign is_compressed_o = (instr_i[1:0] != 2'b11); - always_ff @(posedge clk_i, negedge rst_ni) begin + always_ff @(posedge clk_i or negedge rst_ni) begin if (!rst_ni) begin cm_state_q <= CmIdle; - // The following regs don't need to be reset as they get assigned before first usage: - // cm_rlist_q, cm_sp_offset_q 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; - cm_state_q <= cm_state_d; end end diff --git a/rtl/ibex_if_stage.sv b/rtl/ibex_if_stage.sv index 89faea3a52..ba5c0db53e 100644 --- a/rtl/ibex_if_stage.sv +++ b/rtl/ibex_if_stage.sv @@ -410,7 +410,8 @@ 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 #( - .RV32ZC (RV32ZC) + .RV32ZC (RV32ZC), + .ResetAll (ResetAll) ) compressed_decoder_i ( .clk_i (clk_i), .rst_ni (rst_ni), From c9081f857af3aca70d8804fa957c6385348c490f Mon Sep 17 00:00:00 2001 From: Samuel Riedel Date: Mon, 22 Dec 2025 23:00:31 +0000 Subject: [PATCH 16/16] [formal] Disable the Zc* extensions for formal verification --- dv/formal/check/top.sv | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) 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) );