catalyst-n1 / rtl /rv32i_core.v
mrwabbit's picture
Initial upload: Catalyst N1 open source neuromorphic processor RTL
e4cdd5f verified
// ============================================================================
// 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