Last active
April 17, 2026 15:41
-
-
Save bit-hack/64397fe9aa910f365cd121c681a89c32 to your computer and use it in GitHub Desktop.
rvmini.v
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // | |
| // | |
| // | |
| `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