Skip to content

Instantly share code, notes, and snippets.

@bit-hack
Last active April 17, 2026 15:41
Show Gist options
  • Select an option

  • Save bit-hack/64397fe9aa910f365cd121c681a89c32 to your computer and use it in GitHub Desktop.

Select an option

Save bit-hack/64397fe9aa910f365cd121c681a89c32 to your computer and use it in GitHub Desktop.
rvmini.v
//
//
//
`default_nettype none
`ifdef COSIM
import "DPI-C" function void COSIM_FETCH(input int pc, input int fetch);
import "DPI-C" function void COSIM_WRITE(input int rd, input int data);
import "DPI-C" function void COSIM_INIT(input string path);
`endif
module rvmini(
input iClk,
input iReset,
// memory bus
output reg oValid,
input iReady,
output reg [31:0] oAddr,
output reg [31:0] oData,
input [31:0] iData,
output reg [ 3:0] oWrMask,
output reg oWr,
output reg oRd,
output reg oFetch
);
parameter RV_BOOT_ADDR = 32'h10074;
localparam STAGE_FETCH = 'b00001;
localparam STAGE_DECODE = 'b00010;
localparam STAGE_EXECUTE = 'b00100;
localparam STAGE_SHIFT = 'b01000;
localparam STAGE_WRITEBACK = 'b10000;
reg [31:0] X[32];
reg [31:0] fetch; // lower two bits unused
reg [31:0] pc;
reg [ 4:0] stage;
reg [31:0] aluRes;
reg [ 4:0] shift;
reg [31:0] shVal;
reg shSign;
reg [31:0] retAddr;
// stage
wire isStageFetch = stage[0];
wire isStageDecode = stage[1];
wire isStageExecute = stage[2];
wire isStageShift = stage[3];
wire isStageWriteback = stage[4];
// decode
wire [4:0] opcode = fetch[6:2];
wire [ 4:0] rs1 = fetch[19:15];
wire [ 4:0] rs2 = fetch[24:20];
wire [31:0] xRs1 = X[rs1];
wire [31:0] xRs2 = X[rs2];
wire [2:0] funct3 = fetch[14:12];
wire [6:0] funct7 = fetch[31:25];
wire funct7_5 = funct7[5];
wire [4:0] rd = fetch[11: 7];
wire isLOAD = (opcode == 5'b00000);
wire isOPIMM = (opcode == 5'b00100);
wire isAUIPC = (opcode == 5'b00101);
wire isSTORE = (opcode == 5'b01000);
wire isOP = (opcode == 5'b01100);
wire isLUI = (opcode == 5'b01101);
wire isBRANCH = (opcode == 5'b11000);
wire isJALR = (opcode == 5'b11001);
wire isJAL = (opcode == 5'b11011);
wire [7:0] f3h = 1 << funct3;
wire isBEQ = f3h[0];
wire isBNE = f3h[1];
wire isBLT = f3h[4];
wire isBGE = f3h[5];
wire isBLTU = f3h[6];
wire isBGEU = f3h[7];
wire isADD = f3h[0];
wire isSHL = f3h[1];
wire isSLT = f3h[2]; // unused
wire isSLTU = f3h[3];
wire isXOR = f3h[4];
wire isSHR = f3h[5];
wire isOR = f3h[6];
wire isAND = f3h[7];
wire isLB = f3h[0];
wire isLH = f3h[1];
wire isLW = f3h[2]; // unused
wire isLBU = f3h[4];
wire isLHU = f3h[5];
wire isSB = f3h[0];
wire isSH = f3h[1];
wire isSW = f3h[2]; // unused
// immediates
wire [31:0] immu = {fetch[31], fetch[30:12], {12{1'b0}}};
wire [31:0] immj = {{12{fetch[31]}}, fetch[19:12], fetch[20], fetch[30:21], 1'b0};
wire [31:0] imms = {{21{fetch[31]}}, fetch[30:25], fetch[11:7]};
wire [31:0] immb = {{20{fetch[31]}}, fetch[7], fetch[30:25], fetch[11:8], 1'b0};
wire [31:0] immi = {{21{fetch[31]}}, fetch[30:20]};
wire [31:0] imm =
(isLUI | isAUIPC) ? immu :
(isJAL) ? immj :
(isSTORE) ? imms :
(isBRANCH) ? immb :
immi;
// ALU
wire [31:0] aluLhs = isAUIPC ? pc : xRs1;
wire [31:0] aluRhs = (isOPIMM | isSTORE | isAUIPC | isJALR) ? imm : xRs2;
wire [32:0] aluTmp = { 1'b1, ~aluRhs } + { 1'b0, aluLhs } + 1;
wire [31:0] aluAdd = aluLhs + aluRhs;
wire [31:0] aluSub = aluTmp[31:0];
wire aluEq = (aluSub == 0);
wire aluNeq = (aluSub != 0);
wire aluLt = aluTmp[32] ^ aluLhs[31] ^ aluRhs[31];
wire aluLtu = aluTmp[32];
wire aluGe = !aluLt;
wire aluGeu = !aluLtu;
wire [31:0] aluResMux =
isAUIPC ? aluAdd :
isADD ? ((funct7_5 & isOP) ? aluSub : aluAdd) :
isXOR ? (aluLhs ^ aluRhs) :
isOR ? (aluLhs | aluRhs) :
isAND ? (aluLhs & aluRhs) :
{ 31'd0, isSLT ? aluLt : aluLtu };
// branch
wire taken = isBRANCH & (
(isBEQ & aluEq) |
(isBNE & aluNeq) |
(isBLT & aluLt) |
(isBGE & aluGe) |
(isBLTU & aluLtu) |
(isBGEU & aluGeu));
// note: in the decode state we always want pc+4 which we can then latch to use
// for a potential return address.
wire [31:0] pcInc = (isStageExecute & (isJAL | taken)) ? imm : 4;
wire [31:0] jalrAddr = { aluAdd[31:2], 2'b00 };
wire [31:0] pcNext = isJALR ? jalrAddr : (pc + pcInc);
// shifter
wire [31:0] shlVal =
shift[0] ? { shVal[30:0], 1'b0 } :
shift[1] ? { shVal[29:0], 2'b0 } :
shift[2] ? { shVal[27:0], 4'b0 } :
shift[3] ? { shVal[23:0], 8'b0 } :
{ shVal[15:0], 16'b0 };
wire [31:0] shrVal =
shift[0] ? { { 1{ shSign }}, shVal[31: 1] } :
shift[1] ? { { 2{ shSign }}, shVal[31: 2] } :
shift[2] ? { { 4{ shSign }}, shVal[31: 4] } :
shift[3] ? { { 8{ shSign }}, shVal[31: 8] } :
{ {16{ shSign }}, shVal[31:16] };
wire [4:0] shiftNext = shift & (
shift[0] ? 5'b11110 :
shift[1] ? 5'b11101 :
shift[2] ? 5'b11011 :
shift[3] ? 5'b10111 :
5'b01111);
// load / store
wire [31:0] ldstAddr = xRs1 + imm;
// store
wire [31:0] stMap1 = { xRs2[7:0], xRs2[7:0], xRs2[7:0], xRs2[7:0] };
wire [31:0] stMap2 = { xRs2[15:0], xRs2[15:0] };
wire [31:0] stMap4 = xRs2;
wire [31:0] stData =
isSB ? stMap1 :
isSH ? stMap2 :
stMap4;
wire [3:0] stMask =
isSB ? (4'b0001 << ldstAddr[1:0]) :
isSH ? (4'b0011 << { ldstAddr[1], 1'b0 }) :
4'b1111;
// load
wire [31:0] ldShl =
(ldstAddr[1:0] == 2'd0) ? iData :
(ldstAddr[1:0] == 2'd1) ? { 8'h0, iData[31: 8] } :
(ldstAddr[1:0] == 2'd2) ? { 16'h0, iData[31:16] } :
{ 24'h0, iData[31:24] };
wire [31:0] ldData =
(isLB | isLBU) ? { {24{ isLBU ? 1'b0 : ldShl[ 7] }}, ldShl[ 7:0] } :
(isLH | isLHU) ? { {16{ isLHU ? 1'b0 : ldShl[15] }}, ldShl[15:0] } :
ldShl;
// writeback
wire [31:0] wbVal =
(isJALR | isJAL) ? retAddr :
(isLUI) ? imm :
(isLOAD) ? ldData :
aluRes;
wire wb = (|rd) & !(isBRANCH | isSTORE);
// stage machine
always @(posedge iClk) begin
(* parallel_case *)
case (1'b1)
isStageFetch: begin
stage <= STAGE_DECODE;
oValid <= 1;
oFetch <= 1;
oAddr <= pc;
end
isStageDecode: begin
stage <= STAGE_DECODE;
if (oValid & iReady) begin
retAddr <= pcNext; // note: always pc + 4 in decode stage
oValid <= 0;
oFetch <= 0;
fetch <= iData;
stage <= STAGE_EXECUTE;
`ifdef COSIM
COSIM_FETCH(pc, iData);
`endif
end
end
isStageExecute: begin
stage <= STAGE_WRITEBACK;
// branch
pc <= pcNext;
// alu writeback
aluRes <= aluResMux;
// shifter
shVal <= aluLhs;
shift <= aluRhs[4:0];
shSign <= funct7_5 ? aluLhs[31] : 0;
if ((isOP | isOPIMM) & (isSHL | isSHR)) begin
stage <= STAGE_SHIFT;
end
// memory bus
oAddr <= { ldstAddr[31:2], 2'b0 };
oWrMask <= stMask;
oData <= stData;
oValid <= isSTORE | isLOAD;
oRd <= isLOAD;
oWr <= isSTORE;
oFetch <= 0;
end
isStageShift: begin
if (shift == 0) begin
stage <= STAGE_WRITEBACK;
aluRes <= shVal;
end else begin
shift <= shiftNext;
shVal <= isSHL ? shlVal : shrVal;
end
end
isStageWriteback: begin
stage <= STAGE_FETCH;
if (isLOAD | isSTORE) begin
stage <= STAGE_WRITEBACK;
if (oValid & iReady) begin
oValid <= 0;
oWr <= 0;
oRd <= 0;
stage <= STAGE_FETCH;
end
end
if (wb) begin
X[rd] <= wbVal;
`ifdef COSIM
COSIM_WRITE(32'(rd), wbVal);
`endif
end
end
endcase
if (iReset) begin
stage <= STAGE_FETCH;
pc <= RV_BOOT_ADDR;
oValid <= 0;
end
end
endmodule
`ifdef COSIM
module top(
input iClk,
input iReset
);
reg [31:0] mem['h10000]; // 256Kb
reg [31:0] memRdData;
reg memReady;
integer sigLo, sigHi;
initial begin
static string path;
if ($value$plusargs("program=%s", path)) begin
$readmemh(path, mem);
end else begin
$display("+program required");
$finish;
end
$value$plusargs("siglo=%x", sigLo);
$value$plusargs("sighi=%x", sigHi);
`ifdef COSIM
COSIM_INIT(path);
`endif
end
final begin
integer i;
for (i=sigLo; i<sigHi; i+=4) begin
$display("%x", mem[i >> 2]);
end
end
always @(posedge iClk) begin
if (oValid) begin
if (memReady) begin
// data has been transfered
memReady <= 0;
end else begin
if (oWr) begin
if (oWrMask[0]) mem[ oAddr >> 2 ][ 7: 0] <= oData[ 7: 0];
if (oWrMask[1]) mem[ oAddr >> 2 ][15: 8] <= oData[15: 8];
if (oWrMask[2]) mem[ oAddr >> 2 ][23:16] <= oData[23:16];
if (oWrMask[3]) mem[ oAddr >> 2 ][31:24] <= oData[31:24];
memReady <= 1;
end else begin
memRdData <= mem[ oAddr >> 2 ];
memReady <= 1;
end
end
end
end
wire oValid;
wire [31:0] oAddr;
wire [31:0] oData;
wire [ 3:0] oWrMask;
wire oWr;
wire oRd;
wire oFetch;
rvmini dut(
.iClk (iClk),
.iReset (iReset),
.oValid (oValid),
.iReady (memReady),
.oAddr (oAddr),
.oData (oData),
.iData (memRdData),
.oWrMask(oWrMask),
.oWr (oWr),
.oRd (oRd),
.oFetch (oFetch)
);
endmodule
`endif // COSIM
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment