// ============================================================================ // RV32I Core // ============================================================================ // // Copyright 2026 Henry Arthur Shulayev Barnes / Catalyst Neuromorphic Ltd // Company No. 17054540 — UK Patent Application No. 2602902.6 // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // ============================================================================ `timescale 1ns/1ps module rv32i_core #( parameter IMEM_DEPTH = 65536, parameter IMEM_ADDR_BITS = 16, parameter DMEM_DEPTH = 65536, parameter DMEM_ADDR_BITS = 16 )( input wire clk, input wire rst_n, input wire enable, input wire imem_we, input wire [IMEM_ADDR_BITS-1:0] imem_waddr, input wire [31:0] imem_wdata, output reg mmio_valid, output reg mmio_we, output reg [15:0] mmio_addr, output reg [31:0] mmio_wdata, input wire [31:0] mmio_rdata, input wire mmio_ready, output wire halted, output wire [31:0] pc_out, input wire [31:0] debug_bp_addr_0, input wire [31:0] debug_bp_addr_1, input wire [31:0] debug_bp_addr_2, input wire [31:0] debug_bp_addr_3, input wire [3:0] debug_bp_enable, input wire debug_resume, input wire debug_halt_req, input wire debug_single_step ); reg [31:0] regfile [0:31]; reg [31:0] fregfile [0:31]; reg [31:0] imem [0:IMEM_DEPTH-1]; always @(posedge clk) begin if (imem_we) imem[imem_waddr] <= imem_wdata; end reg [31:0] dmem [0:DMEM_DEPTH-1]; reg [31:0] pc; reg [31:0] instr; reg fetch_valid; reg halt_r; assign pc_out = pc; assign halted = halt_r; wire [IMEM_ADDR_BITS-1:0] pc_word = pc[IMEM_ADDR_BITS+1:2]; wire [31:0] fetched_instr = imem[pc_word]; wire [6:0] opcode = instr[6:0]; wire [4:0] rd = instr[11:7]; wire [2:0] funct3 = instr[14:12]; wire [4:0] rs1 = instr[19:15]; wire [4:0] rs2 = instr[24:20]; wire [6:0] funct7 = instr[31:25]; wire [31:0] imm_i = {{20{instr[31]}}, instr[31:20]}; wire [31:0] imm_s = {{20{instr[31]}}, instr[31:25], instr[11:7]}; wire [31:0] imm_b = {{19{instr[31]}}, instr[31], instr[7], instr[30:25], instr[11:8], 1'b0}; wire [31:0] imm_u = {instr[31:12], 12'b0}; wire [31:0] imm_j = {{11{instr[31]}}, instr[31], instr[19:12], instr[20], instr[30:21], 1'b0}; wire [31:0] rs1_val = (rs1 == 5'd0) ? 32'd0 : regfile[rs1]; wire [31:0] rs2_val = (rs2 == 5'd0) ? 32'd0 : regfile[rs2]; localparam OP_LUI = 7'b0110111; localparam OP_AUIPC = 7'b0010111; localparam OP_JAL = 7'b1101111; localparam OP_JALR = 7'b1100111; localparam OP_BRANCH = 7'b1100011; localparam OP_LOAD = 7'b0000011; localparam OP_STORE = 7'b0100011; localparam OP_IMM = 7'b0010011; localparam OP_REG = 7'b0110011; localparam OP_FENCE = 7'b0001111; localparam OP_SYSTEM = 7'b1110011; localparam OP_FLW = 7'b0000111; localparam OP_FSW = 7'b0100111; localparam OP_FP = 7'b1010011; function real f32_to_real; input [31:0] f; reg [63:0] d; begin if (f[30:0] == 31'd0) begin d = {f[31], 63'd0}; end else if (f[30:23] == 8'hFF) begin d = {f[31], 11'h7FF, f[22:0], 29'd0}; end else begin d[63] = f[31]; d[62:52] = {3'd0, f[30:23]} + 11'd896; d[51:0] = {f[22:0], 29'd0}; end f32_to_real = $bitstoreal(d); end endfunction function [31:0] real_to_f32; input real r; reg [63:0] d; reg [10:0] dexp; reg [7:0] fexp; begin d = $realtobits(r); if (d[62:0] == 63'd0) begin real_to_f32 = {d[63], 31'd0}; end else begin dexp = d[62:52]; if (dexp >= 11'd1151) begin real_to_f32 = {d[63], 8'hFF, 23'd0}; end else if (dexp <= 11'd896) begin real_to_f32 = {d[63], 31'd0}; end else begin fexp = dexp - 11'd896; real_to_f32 = {d[63], fexp, d[51:29]}; end end end endfunction function real fp_sqrt; input real x; real guess; integer i; begin if (x <= 0.0) begin fp_sqrt = 0.0; end else begin guess = x; for (i = 0; i < 25; i = i + 1) guess = (guess + x / guess) / 2.0; fp_sqrt = guess; end end endfunction wire is_muldiv = (opcode == OP_REG) && (funct7 == 7'b0000001); wire signed [63:0] mul_ss = $signed(rs1_val) * $signed(rs2_val); wire [63:0] mul_uu = rs1_val * rs2_val; wire signed [63:0] mul_su = $signed(rs1_val) * $signed({1'b0, rs2_val}); wire signed [31:0] div_s = (rs2_val == 0) ? -32'sd1 : (rs1_val == 32'h80000000 && rs2_val == 32'hFFFFFFFF) ? 32'h80000000 : $signed(rs1_val) / $signed(rs2_val); wire [31:0] div_u = (rs2_val == 0) ? 32'hFFFFFFFF : rs1_val / rs2_val; wire signed [31:0] rem_s = (rs2_val == 0) ? $signed(rs1_val) : (rs1_val == 32'h80000000 && rs2_val == 32'hFFFFFFFF) ? 32'sd0 : $signed(rs1_val) % $signed(rs2_val); wire [31:0] rem_u = (rs2_val == 0) ? rs1_val : rs1_val % rs2_val; reg [31:0] muldiv_result; always @(*) begin case (funct3) 3'b000: muldiv_result = mul_ss[31:0]; 3'b001: muldiv_result = mul_ss[63:32]; 3'b010: muldiv_result = mul_su[63:32]; 3'b011: muldiv_result = mul_uu[63:32]; 3'b100: muldiv_result = div_s; 3'b101: muldiv_result = div_u; 3'b110: muldiv_result = rem_s; 3'b111: muldiv_result = rem_u; endcase end reg [31:0] csr_mtvec; reg [31:0] csr_mepc; reg [31:0] csr_mcause; reg [31:0] csr_mstatus; reg [31:0] csr_mie; reg [31:0] csr_mip; reg [63:0] csr_mcycle; reg [63:0] csr_mtimecmp; localparam CSR_MSTATUS = 12'h300; localparam CSR_MIE = 12'h304; localparam CSR_MTVEC = 12'h305; localparam CSR_MEPC = 12'h341; localparam CSR_MCAUSE = 12'h342; localparam CSR_MIP = 12'h344; localparam CSR_MCYCLE = 12'hB00; localparam CSR_MCYCLEH = 12'hB80; localparam CSR_MTIMECMP = 12'h7C0; localparam CSR_MTIMECMPH = 12'h7C1; wire [11:0] csr_addr = instr[31:20]; wire [4:0] csr_zimm = rs1; reg [31:0] csr_rdata; always @(*) begin case (csr_addr) CSR_MSTATUS: csr_rdata = csr_mstatus; CSR_MIE: csr_rdata = csr_mie; CSR_MTVEC: csr_rdata = csr_mtvec; CSR_MEPC: csr_rdata = csr_mepc; CSR_MCAUSE: csr_rdata = csr_mcause; CSR_MIP: csr_rdata = csr_mip; CSR_MCYCLE: csr_rdata = csr_mcycle[31:0]; CSR_MCYCLEH: csr_rdata = csr_mcycle[63:32]; CSR_MTIMECMP: csr_rdata = csr_mtimecmp[31:0]; CSR_MTIMECMPH:csr_rdata = csr_mtimecmp[63:32]; default: csr_rdata = 32'd0; endcase end wire timer_pending = (csr_mcycle >= csr_mtimecmp); wire timer_irq = timer_pending && csr_mstatus[3] && csr_mie[7]; wire [31:0] alu_b = (opcode == OP_REG) ? rs2_val : imm_i; wire [4:0] shamt = alu_b[4:0]; reg [31:0] alu_result; always @(*) begin case (funct3) 3'b000: alu_result = (opcode == OP_REG && funct7[5]) ? (rs1_val - rs2_val) : (rs1_val + alu_b); 3'b001: alu_result = rs1_val << shamt; 3'b010: alu_result = ($signed(rs1_val) < $signed(alu_b)) ? 32'd1 : 32'd0; 3'b011: alu_result = (rs1_val < alu_b) ? 32'd1 : 32'd0; 3'b100: alu_result = rs1_val ^ alu_b; 3'b101: alu_result = funct7[5] ? ($signed(rs1_val) >>> shamt) : (rs1_val >> shamt); 3'b110: alu_result = rs1_val | alu_b; 3'b111: alu_result = rs1_val & alu_b; default: alu_result = 32'd0; endcase end reg branch_taken; always @(*) begin case (funct3) 3'b000: branch_taken = (rs1_val == rs2_val); 3'b001: branch_taken = (rs1_val != rs2_val); 3'b100: branch_taken = ($signed(rs1_val) < $signed(rs2_val)); 3'b101: branch_taken = ($signed(rs1_val) >= $signed(rs2_val)); 3'b110: branch_taken = (rs1_val < rs2_val); 3'b111: branch_taken = (rs1_val >= rs2_val); default: branch_taken = 1'b0; endcase end wire [31:0] mem_addr = rs1_val + ((opcode == OP_STORE) ? imm_s : imm_i); wire is_mmio = (mem_addr[31:16] == 16'hFFFF); wire [DMEM_ADDR_BITS-1:0] dmem_word_addr = mem_addr[DMEM_ADDR_BITS+1:2]; localparam S_FETCH = 4'd0; localparam S_EXEC = 4'd1; localparam S_MEM_RD = 4'd2; localparam S_MEM_WR = 4'd3; localparam S_HALT = 4'd4; localparam S_TRAP = 4'd5; localparam S_DEBUG_HALT = 4'd6; reg [3:0] state; reg debug_single_step_pending; wire bp_match = (debug_bp_enable[0] && (pc == debug_bp_addr_0)) || (debug_bp_enable[1] && (pc == debug_bp_addr_1)) || (debug_bp_enable[2] && (pc == debug_bp_addr_2)) || (debug_bp_enable[3] && (pc == debug_bp_addr_3)); real fp_op_a, fp_op_b, fp_op_r; reg mem_rd_is_float; integer ri; always @(posedge clk or negedge rst_n) begin if (!rst_n) begin pc <= 32'd0; instr <= 32'd0; fetch_valid <= 1'b0; halt_r <= 1'b0; state <= S_FETCH; mmio_valid <= 1'b0; mmio_we <= 1'b0; mmio_addr <= 16'd0; mmio_wdata <= 32'd0; csr_mtvec <= 32'd0; csr_mepc <= 32'd0; csr_mcause <= 32'd0; csr_mstatus <= 32'd0; csr_mie <= 32'd0; csr_mip <= 32'd0; csr_mcycle <= 64'd0; csr_mtimecmp <= 64'hFFFFFFFF_FFFFFFFF; mem_rd_is_float <= 1'b0; debug_single_step_pending <= 1'b0; for (ri = 0; ri < 32; ri = ri + 1) begin regfile[ri] <= 32'd0; fregfile[ri] <= 32'd0; end end else if (!enable) begin state <= S_FETCH; pc <= 32'd0; halt_r <= 1'b0; mmio_valid <= 1'b0; mem_rd_is_float <= 1'b0; csr_mcycle <= 64'd0; debug_single_step_pending <= 1'b0; end else begin csr_mcycle <= csr_mcycle + 64'd1; csr_mip[7] <= timer_pending; case (state) S_FETCH: begin if (debug_halt_req) begin halt_r <= 1'b1; state <= S_DEBUG_HALT; end else if (bp_match) begin halt_r <= 1'b1; state <= S_DEBUG_HALT; end else if (debug_single_step_pending) begin debug_single_step_pending <= 1'b0; halt_r <= 1'b1; state <= S_DEBUG_HALT; end else if (timer_irq) begin csr_mepc <= pc; csr_mcause <= 32'h80000007; csr_mstatus[3] <= 1'b0; csr_mstatus[7] <= csr_mstatus[3]; pc <= csr_mtvec & ~32'd3; state <= S_FETCH; end else begin instr <= fetched_instr; fetch_valid <= 1'b1; state <= S_EXEC; end end S_EXEC: begin mmio_valid <= 1'b0; case (opcode) OP_LUI: begin if (rd != 0) regfile[rd] <= imm_u; pc <= pc + 4; state <= S_FETCH; end OP_AUIPC: begin if (rd != 0) regfile[rd] <= pc + imm_u; pc <= pc + 4; state <= S_FETCH; end OP_JAL: begin if (rd != 0) regfile[rd] <= pc + 4; pc <= pc + imm_j; state <= S_FETCH; end OP_JALR: begin if (rd != 0) regfile[rd] <= pc + 4; pc <= (rs1_val + imm_i) & ~32'd1; state <= S_FETCH; end OP_BRANCH: begin pc <= branch_taken ? (pc + imm_b) : (pc + 4); state <= S_FETCH; end OP_LOAD: begin if (is_mmio) begin mmio_valid <= 1'b1; mmio_we <= 1'b0; mmio_addr <= mem_addr[15:0]; mem_rd_is_float <= 1'b0; state <= S_MEM_RD; end else begin if (rd != 0) begin case (funct3) 3'b000: begin case (mem_addr[1:0]) 2'd0: regfile[rd] <= {{24{dmem[dmem_word_addr][7]}}, dmem[dmem_word_addr][7:0]}; 2'd1: regfile[rd] <= {{24{dmem[dmem_word_addr][15]}}, dmem[dmem_word_addr][15:8]}; 2'd2: regfile[rd] <= {{24{dmem[dmem_word_addr][23]}}, dmem[dmem_word_addr][23:16]}; 2'd3: regfile[rd] <= {{24{dmem[dmem_word_addr][31]}}, dmem[dmem_word_addr][31:24]}; endcase end 3'b001: begin if (mem_addr[1]) regfile[rd] <= {{16{dmem[dmem_word_addr][31]}}, dmem[dmem_word_addr][31:16]}; else regfile[rd] <= {{16{dmem[dmem_word_addr][15]}}, dmem[dmem_word_addr][15:0]}; end 3'b010: regfile[rd] <= dmem[dmem_word_addr]; 3'b100: begin case (mem_addr[1:0]) 2'd0: regfile[rd] <= {24'd0, dmem[dmem_word_addr][7:0]}; 2'd1: regfile[rd] <= {24'd0, dmem[dmem_word_addr][15:8]}; 2'd2: regfile[rd] <= {24'd0, dmem[dmem_word_addr][23:16]}; 2'd3: regfile[rd] <= {24'd0, dmem[dmem_word_addr][31:24]}; endcase end 3'b101: begin if (mem_addr[1]) regfile[rd] <= {16'd0, dmem[dmem_word_addr][31:16]}; else regfile[rd] <= {16'd0, dmem[dmem_word_addr][15:0]}; end default: ; endcase end pc <= pc + 4; state <= S_FETCH; end end OP_STORE: begin if (is_mmio) begin mmio_valid <= 1'b1; mmio_we <= 1'b1; mmio_addr <= mem_addr[15:0]; mmio_wdata <= rs2_val; state <= S_MEM_WR; end else begin case (funct3) 3'b000: begin case (mem_addr[1:0]) 2'd0: dmem[dmem_word_addr][7:0] <= rs2_val[7:0]; 2'd1: dmem[dmem_word_addr][15:8] <= rs2_val[7:0]; 2'd2: dmem[dmem_word_addr][23:16] <= rs2_val[7:0]; 2'd3: dmem[dmem_word_addr][31:24] <= rs2_val[7:0]; endcase end 3'b001: begin if (mem_addr[1]) dmem[dmem_word_addr][31:16] <= rs2_val[15:0]; else dmem[dmem_word_addr][15:0] <= rs2_val[15:0]; end 3'b010: dmem[dmem_word_addr] <= rs2_val; default: ; endcase pc <= pc + 4; state <= S_FETCH; end end OP_IMM: begin if (rd != 0) regfile[rd] <= alu_result; pc <= pc + 4; state <= S_FETCH; end OP_REG: begin if (is_muldiv) begin if (rd != 0) regfile[rd] <= muldiv_result; end else begin if (rd != 0) regfile[rd] <= alu_result; end pc <= pc + 4; state <= S_FETCH; end OP_FENCE: begin pc <= pc + 4; state <= S_FETCH; end OP_SYSTEM: begin if (funct3 == 3'b000) begin if (instr[31:20] == 12'h302) begin pc <= csr_mepc; csr_mstatus[3] <= csr_mstatus[7]; csr_mstatus[7] <= 1'b1; state <= S_FETCH; end else begin halt_r <= 1'b1; state <= S_HALT; end end else begin if (rd != 0) regfile[rd] <= csr_rdata; case (funct3) 3'b001: begin case (csr_addr) CSR_MSTATUS: csr_mstatus <= rs1_val; CSR_MIE: csr_mie <= rs1_val; CSR_MTVEC: csr_mtvec <= rs1_val; CSR_MEPC: csr_mepc <= rs1_val; CSR_MCAUSE: csr_mcause <= rs1_val; CSR_MTIMECMP: csr_mtimecmp[31:0] <= rs1_val; CSR_MTIMECMPH:csr_mtimecmp[63:32] <= rs1_val; default: ; endcase end 3'b010: begin if (rs1 != 0) begin case (csr_addr) CSR_MSTATUS: csr_mstatus <= csr_mstatus | rs1_val; CSR_MIE: csr_mie <= csr_mie | rs1_val; CSR_MTVEC: csr_mtvec <= csr_mtvec | rs1_val; default: ; endcase end end 3'b011: begin if (rs1 != 0) begin case (csr_addr) CSR_MSTATUS: csr_mstatus <= csr_mstatus & ~rs1_val; CSR_MIE: csr_mie <= csr_mie & ~rs1_val; default: ; endcase end end 3'b101: begin case (csr_addr) CSR_MSTATUS: csr_mstatus <= {27'd0, csr_zimm}; CSR_MIE: csr_mie <= {27'd0, csr_zimm}; CSR_MTVEC: csr_mtvec <= {27'd0, csr_zimm}; default: ; endcase end 3'b110: begin if (csr_zimm != 0) begin case (csr_addr) CSR_MSTATUS: csr_mstatus <= csr_mstatus | {27'd0, csr_zimm}; CSR_MIE: csr_mie <= csr_mie | {27'd0, csr_zimm}; default: ; endcase end end 3'b111: begin if (csr_zimm != 0) begin case (csr_addr) CSR_MSTATUS: csr_mstatus <= csr_mstatus & ~{27'd0, csr_zimm}; CSR_MIE: csr_mie <= csr_mie & ~{27'd0, csr_zimm}; default: ; endcase end end default: ; endcase pc <= pc + 4; state <= S_FETCH; end end OP_FLW: begin if (is_mmio) begin mmio_valid <= 1'b1; mmio_we <= 1'b0; mmio_addr <= mem_addr[15:0]; mem_rd_is_float <= 1'b1; state <= S_MEM_RD; end else begin fregfile[rd] <= dmem[dmem_word_addr]; pc <= pc + 4; state <= S_FETCH; end end OP_FSW: begin if (is_mmio) begin mmio_valid <= 1'b1; mmio_we <= 1'b1; mmio_addr <= mem_addr[15:0]; mmio_wdata <= fregfile[rs2]; state <= S_MEM_WR; end else begin dmem[dmem_word_addr] <= fregfile[rs2]; pc <= pc + 4; state <= S_FETCH; end end OP_FP: begin case (funct7) 7'b0000000: begin fp_op_a = f32_to_real(fregfile[rs1]); fp_op_b = f32_to_real(fregfile[rs2]); fregfile[rd] <= real_to_f32(fp_op_a + fp_op_b); end 7'b0000100: begin fp_op_a = f32_to_real(fregfile[rs1]); fp_op_b = f32_to_real(fregfile[rs2]); fregfile[rd] <= real_to_f32(fp_op_a - fp_op_b); end 7'b0001000: begin fp_op_a = f32_to_real(fregfile[rs1]); fp_op_b = f32_to_real(fregfile[rs2]); fregfile[rd] <= real_to_f32(fp_op_a * fp_op_b); end 7'b0001100: begin fp_op_a = f32_to_real(fregfile[rs1]); fp_op_b = f32_to_real(fregfile[rs2]); if (fp_op_b != 0.0) fregfile[rd] <= real_to_f32(fp_op_a / fp_op_b); else fregfile[rd] <= 32'h7FC00000; end 7'b0101100: begin fp_op_a = f32_to_real(fregfile[rs1]); fp_op_r = fp_sqrt(fp_op_a); fregfile[rd] <= real_to_f32(fp_op_r); end 7'b0010100: begin fp_op_a = f32_to_real(fregfile[rs1]); fp_op_b = f32_to_real(fregfile[rs2]); case (funct3) 3'b000: fregfile[rd] <= (fp_op_a <= fp_op_b) ? fregfile[rs1] : fregfile[rs2]; 3'b001: fregfile[rd] <= (fp_op_a >= fp_op_b) ? fregfile[rs1] : fregfile[rs2]; default: ; endcase end 7'b0010000: begin case (funct3) 3'b000: fregfile[rd] <= {fregfile[rs2][31], fregfile[rs1][30:0]}; 3'b001: fregfile[rd] <= {~fregfile[rs2][31], fregfile[rs1][30:0]}; 3'b010: fregfile[rd] <= {fregfile[rs1][31] ^ fregfile[rs2][31], fregfile[rs1][30:0]}; default: ; endcase end 7'b1100000: begin fp_op_a = f32_to_real(fregfile[rs1]); if (rd != 0) regfile[rd] <= $rtoi(fp_op_a); end 7'b1101000: begin fregfile[rd] <= real_to_f32($itor($signed(rs1_val))); end 7'b1010000: begin fp_op_a = f32_to_real(fregfile[rs1]); fp_op_b = f32_to_real(fregfile[rs2]); if (rd != 0) begin case (funct3) 3'b010: regfile[rd] <= (fp_op_a == fp_op_b) ? 32'd1 : 32'd0; 3'b001: regfile[rd] <= (fp_op_a < fp_op_b) ? 32'd1 : 32'd0; 3'b000: regfile[rd] <= (fp_op_a <= fp_op_b) ? 32'd1 : 32'd0; default: ; endcase end end 7'b1110000: begin if (rd != 0) regfile[rd] <= fregfile[rs1]; end 7'b1111000: begin fregfile[rd] <= rs1_val; end default: ; endcase pc <= pc + 4; state <= S_FETCH; end default: begin halt_r <= 1'b1; state <= S_HALT; end endcase end S_MEM_RD: begin if (mmio_ready) begin mmio_valid <= 1'b0; if (mem_rd_is_float) begin fregfile[rd] <= mmio_rdata; mem_rd_is_float <= 1'b0; end else begin if (rd != 0) regfile[rd] <= mmio_rdata; end pc <= pc + 4; state <= S_FETCH; end end S_MEM_WR: begin if (mmio_ready) begin mmio_valid <= 1'b0; pc <= pc + 4; state <= S_FETCH; end end S_HALT: begin end S_DEBUG_HALT: begin if (debug_resume) begin halt_r <= 1'b0; state <= S_FETCH; end else if (debug_single_step) begin halt_r <= 1'b0; debug_single_step_pending <= 1'b1; state <= S_FETCH; end end default: state <= S_HALT; endcase end end endmodule