WIP: blinky to RISC-V tutorial.
This commit is contained in:
@@ -5,8 +5,8 @@
|
||||
`define NRV_IO_LEDS // Mapped IO, LEDs D1,D2,D3,D4 (D5 is used to display errors)
|
||||
`define NRV_IO_IRDA // In IO_LEDS, support for the IRDA on the IceStick (WIP)
|
||||
`define NRV_IO_UART // Mapped IO, virtual UART (USB)
|
||||
`define NRV_IO_SSD1351 // Mapped IO, 128x128x64K OLED screen
|
||||
//`define NRV_IO_MAX7219 // Mapped IO, 8x8 led matrix
|
||||
//`define NRV_IO_SSD1351 // Mapped IO, 128x128x64K OLED screen
|
||||
`define NRV_IO_MAX7219 // Mapped IO, 8x8 led matrix
|
||||
`define NRV_MAPPED_SPI_FLASH // SPI flash mapped in address space. Can be used to run code from SPI flash.
|
||||
|
||||
/************************* Processor configuration *******************************************************************/
|
||||
|
||||
5
FemtoRV/TUTORIALS/FROM_BLINKER_TO_RISCV/README.md
Normal file
5
FemtoRV/TUTORIALS/FROM_BLINKER_TO_RISCV/README.md
Normal file
@@ -0,0 +1,5 @@
|
||||
# From Blinker to RISC-V
|
||||
|
||||
A progressive journey from a simple blinky design to a RISC-V core.
|
||||
|
||||
_WIP_
|
||||
417
FemtoRV/TUTORIALS/FROM_BLINKER_TO_RISCV/riscv_assembly.v
Normal file
417
FemtoRV/TUTORIALS/FROM_BLINKER_TO_RISCV/riscv_assembly.v
Normal file
@@ -0,0 +1,417 @@
|
||||
/*
|
||||
* A simple assembler for RiscV written in VERILOG.
|
||||
* See table page 104 of RiscV instruction manual.
|
||||
* Bruno Levy, March 2022
|
||||
*/
|
||||
|
||||
integer romPC;
|
||||
initial romPC = 0;
|
||||
|
||||
/***************************************************************************/
|
||||
|
||||
/*
|
||||
* Register names.
|
||||
* Looks stupid, but makes assembly code more legible (without it,
|
||||
* one does not make the difference between immediate values and
|
||||
* register ids).
|
||||
*/
|
||||
|
||||
localparam x0 = 0, x1 = 1, x2 = 2, x3 = 3, x4 = 4, x5 = 5, x6 = 6, x7 = 7,
|
||||
x8 = 8, x9 = 8, x10=10, x11=11, x12=12, x13=13, x14=14, x15=15,
|
||||
x16=16, x17=17, x18=18, x19=19, x20=20, x21=21, x22=22, x23=23,
|
||||
x24=24, x25=25, x26=26, x27=27, x28=28, x29=29, x30=30, x31=31;
|
||||
|
||||
|
||||
localparam [31:0] NOP_CODEOP = 32'b0000000_00000_00000_000_00000_0110011; // add x0,x0,x0
|
||||
|
||||
/***************************************************************************/
|
||||
|
||||
/*
|
||||
* R-Type instructions.
|
||||
* rd <- rs1 OP rs2
|
||||
*/
|
||||
|
||||
task RType;
|
||||
input [6:0] opcode;
|
||||
input [4:0] rd;
|
||||
input [4:0] rs1;
|
||||
input [4:0] rs2;
|
||||
input [2:0] funct3;
|
||||
input [6:0] funct7;
|
||||
begin
|
||||
ROM[romPC[31:2]] = {funct7, rs2, rs1, funct3, rd, opcode};
|
||||
romPC = romPC + 4;
|
||||
end
|
||||
endtask
|
||||
|
||||
task ADD;
|
||||
input [4:0] rd;
|
||||
input [4:0] rs1;
|
||||
input [4:0] rs2;
|
||||
RType(7'b0110011, rd, rs1, rs2, 3'b000, 7'b0000000);
|
||||
endtask
|
||||
|
||||
task SUB;
|
||||
input [4:0] rd;
|
||||
input [4:0] rs1;
|
||||
input [4:0] rs2;
|
||||
RType(7'b0110011, rd, rs1, rs2, 3'b000, 7'b0100000);
|
||||
endtask
|
||||
|
||||
task SLL;
|
||||
input [4:0] rd;
|
||||
input [4:0] rs1;
|
||||
input [4:0] rs2;
|
||||
RType(7'b0110011, rd, rs1, rs2, 3'b001, 7'b0000000);
|
||||
endtask
|
||||
|
||||
task SLT;
|
||||
input [4:0] rd;
|
||||
input [4:0] rs1;
|
||||
input [4:0] rs2;
|
||||
RType(7'b0110011, rd, rs1, rs2, 3'b010, 7'b0000000);
|
||||
endtask
|
||||
|
||||
task SLTU;
|
||||
input [4:0] rd;
|
||||
input [4:0] rs1;
|
||||
input [4:0] rs2;
|
||||
RType(7'b0110011, rd, rs1, rs2, 3'b011, 7'b0000000);
|
||||
endtask
|
||||
|
||||
task XOR;
|
||||
input [4:0] rd;
|
||||
input [4:0] rs1;
|
||||
input [4:0] rs2;
|
||||
RType(7'b0110011, rd, rs1, rs2, 3'b100, 7'b0000000);
|
||||
endtask
|
||||
|
||||
task SRL;
|
||||
input [4:0] rd;
|
||||
input [4:0] rs1;
|
||||
input [4:0] rs2;
|
||||
RType(7'b0110011, rd, rs1, rs2, 3'b101, 7'b0000000);
|
||||
endtask
|
||||
|
||||
task SRA;
|
||||
input [4:0] rd;
|
||||
input [4:0] rs1;
|
||||
input [4:0] rs2;
|
||||
RType(7'b0110011, rd, rs1, rs2, 3'b101, 7'b0000010);
|
||||
endtask
|
||||
|
||||
task OR;
|
||||
input [4:0] rd;
|
||||
input [4:0] rs1;
|
||||
input [4:0] rs2;
|
||||
RType(7'b0110011, rd, rs1, rs2, 3'b110, 7'b0000000);
|
||||
endtask
|
||||
|
||||
task AND;
|
||||
input [4:0] rd;
|
||||
input [4:0] rs1;
|
||||
input [4:0] rs2;
|
||||
RType(7'b0110011, rd, rs1, rs2, 3'b111, 7'b0000000);
|
||||
endtask
|
||||
|
||||
/***************************************************************************/
|
||||
|
||||
/*
|
||||
* I-Type instructions.
|
||||
* rd <- rs1 OP imm
|
||||
*/
|
||||
|
||||
task IType;
|
||||
input [6:0] opcode;
|
||||
input [4:0] rd;
|
||||
input [4:0] rs1;
|
||||
input [31:0] imm;
|
||||
input [2:0] funct3;
|
||||
begin
|
||||
ROM[romPC[31:2]] = {imm[11:0], rs1, funct3, rd, opcode};
|
||||
romPC = romPC + 4;
|
||||
end
|
||||
endtask
|
||||
|
||||
task ADDI;
|
||||
input [4:0] rd;
|
||||
input [4:0] rs1;
|
||||
input [31:0] imm;
|
||||
begin
|
||||
IType(7'b0010011, rd, rs1, imm, 3'b000);
|
||||
end
|
||||
endtask
|
||||
|
||||
task SLTI;
|
||||
input [4:0] rd;
|
||||
input [4:0] rs1;
|
||||
input [31:0] imm;
|
||||
begin
|
||||
IType(7'b0010011, rd, rs1, imm, 3'b010);
|
||||
end
|
||||
endtask
|
||||
|
||||
task SLTIU;
|
||||
input [4:0] rd;
|
||||
input [4:0] rs1;
|
||||
input [31:0] imm;
|
||||
begin
|
||||
IType(7'b0010011, rd, rs1, imm, 3'b011);
|
||||
end
|
||||
endtask
|
||||
|
||||
task XORI;
|
||||
input [4:0] rd;
|
||||
input [4:0] rs1;
|
||||
input [31:0] imm;
|
||||
begin
|
||||
IType(7'b0010011, rd, rs1, imm, 3'b100);
|
||||
end
|
||||
endtask
|
||||
|
||||
task ORI;
|
||||
input [4:0] rd;
|
||||
input [4:0] rs1;
|
||||
input [31:0] imm;
|
||||
begin
|
||||
IType(7'b0010011, rd, rs1, imm, 3'b110);
|
||||
end
|
||||
endtask
|
||||
|
||||
task ANDI;
|
||||
input [4:0] rd;
|
||||
input [4:0] rs1;
|
||||
input [31:0] imm;
|
||||
begin
|
||||
IType(7'b0010011, rd, rs1, imm, 3'b111);
|
||||
end
|
||||
endtask
|
||||
|
||||
// The three shifts, SLLI, SRLI, SRAI, encoded in RType format
|
||||
// (rs2 is replaced with shift amount=imm[4:0])
|
||||
|
||||
task SLLI;
|
||||
input [4:0] rd;
|
||||
input [4:0] rs1;
|
||||
input [31:0] imm;
|
||||
begin
|
||||
RType(7'b0010011, rd, rs1, imm[4:0], 3'b001, 7'b0000000);
|
||||
end
|
||||
endtask
|
||||
|
||||
task SRLI;
|
||||
input [4:0] rd;
|
||||
input [4:0] rs1;
|
||||
input [31:0] imm;
|
||||
begin
|
||||
RType(7'b0010011, rd, rs1, imm[4:0], 3'b101, 7'b0000000);
|
||||
end
|
||||
endtask
|
||||
|
||||
task SRAI;
|
||||
input [4:0] rd;
|
||||
input [4:0] rs1;
|
||||
input [31:0] imm;
|
||||
begin
|
||||
RType(7'b0010011, rd, rs1, imm[4:0], 3'b101, 7'b0100000);
|
||||
end
|
||||
endtask
|
||||
|
||||
/***************************************************************************/
|
||||
|
||||
/*
|
||||
* Jumps (JAL and JALR)
|
||||
*/
|
||||
|
||||
task JType;
|
||||
input [6:0] opcode;
|
||||
input [4:0] rd;
|
||||
input [31:0] imm;
|
||||
begin
|
||||
ROM[romPC[31:2]] = {imm[20], imm[10:1], imm[11], imm[19:12], rd, opcode};
|
||||
romPC = romPC + 4;
|
||||
end
|
||||
endtask
|
||||
|
||||
task JAL;
|
||||
input [4:0] rd;
|
||||
input [31:0] imm;
|
||||
begin
|
||||
$display("JAL, imm = %d",imm);
|
||||
JType(7'b1101111, rd, imm);
|
||||
end
|
||||
endtask
|
||||
|
||||
// JALR is encoded in the IType format.
|
||||
|
||||
task JALR;
|
||||
input [4:0] rd;
|
||||
input [4:0] rs1;
|
||||
input [31:0] imm;
|
||||
begin
|
||||
IType(7'b1100111, rd, rs1, imm, 3'b000);
|
||||
end
|
||||
endtask
|
||||
|
||||
/***************************************************************************/
|
||||
|
||||
/*
|
||||
* Branch instructions.
|
||||
*/
|
||||
|
||||
task BType;
|
||||
input [6:0] opcode;
|
||||
input [4:0] rs1;
|
||||
input [4:0] rs2;
|
||||
input [31:0] imm;
|
||||
input [2:0] funct3;
|
||||
begin
|
||||
ROM[romPC[31:2]] = {imm[12],imm[10:5], rs2, rs1, funct3, imm[4:1], imm[11], opcode};
|
||||
romPC = romPC + 4;
|
||||
end
|
||||
endtask
|
||||
|
||||
task BEQ;
|
||||
input [4:0] rs1;
|
||||
input [4:0] rs2;
|
||||
input [31:0] imm;
|
||||
begin
|
||||
BType(7'b1100011, rs1, rs2, imm, 3'b000);
|
||||
end
|
||||
endtask
|
||||
|
||||
task BNE;
|
||||
input [4:0] rs1;
|
||||
input [4:0] rs2;
|
||||
input [31:0] imm;
|
||||
begin
|
||||
BType(7'b1100011, rs1, rs2, imm, 3'b001);
|
||||
end
|
||||
endtask
|
||||
|
||||
task BLT;
|
||||
input [4:0] rs1;
|
||||
input [4:0] rs2;
|
||||
input [31:0] imm;
|
||||
begin
|
||||
BType(7'b1100011, rs1, rs2, imm, 3'b100);
|
||||
end
|
||||
endtask
|
||||
|
||||
task BGE;
|
||||
input [4:0] rs1;
|
||||
input [4:0] rs2;
|
||||
input [31:0] imm;
|
||||
begin
|
||||
BType(7'b1100011, rs1, rs2, imm, 3'b101);
|
||||
end
|
||||
endtask
|
||||
|
||||
task BLTU;
|
||||
input [4:0] rs1;
|
||||
input [4:0] rs2;
|
||||
input [31:0] imm;
|
||||
begin
|
||||
BType(7'b1100011, rs1, rs2, imm, 3'b110);
|
||||
end
|
||||
endtask
|
||||
|
||||
task BGEU;
|
||||
input [4:0] rs1;
|
||||
input [4:0] rs2;
|
||||
input [31:0] imm;
|
||||
begin
|
||||
BType(7'b1100011, rs1, rs2, imm, 3'b111);
|
||||
end
|
||||
endtask
|
||||
|
||||
/***************************************************************************/
|
||||
|
||||
/*
|
||||
* LUI and AUIPC
|
||||
*/
|
||||
|
||||
task UType;
|
||||
input [6:0] opcode;
|
||||
input [4:0] rd;
|
||||
input [31:0] imm;
|
||||
begin
|
||||
ROM[romPC[31:2]] = {imm[31:12], rd, opcode};
|
||||
romPC = romPC + 4;
|
||||
end
|
||||
endtask
|
||||
|
||||
task LUI;
|
||||
input [4:0] rd;
|
||||
input [31:0] imm;
|
||||
begin
|
||||
UType(7'b0110111, rd, imm);
|
||||
end
|
||||
endtask
|
||||
|
||||
task AUIPC;
|
||||
input [4:0] rd;
|
||||
input [31:0] imm;
|
||||
begin
|
||||
UType(7'b0010111, rd, imm);
|
||||
end
|
||||
endtask
|
||||
|
||||
/***************************************************************************/
|
||||
|
||||
/*
|
||||
* SYSTEM
|
||||
* (for now, just EBREAK)
|
||||
*/
|
||||
|
||||
task EBREAK;
|
||||
begin
|
||||
ROM[romPC[31:2]] = {12'b000000000001, 5'b00000, 3'b000, 5'b00000, 7'b1110011};
|
||||
romPC = romPC + 4;
|
||||
end
|
||||
endtask
|
||||
|
||||
/***************************************************************************/
|
||||
|
||||
/*
|
||||
* Labels.
|
||||
* Example of usage:
|
||||
*
|
||||
* ADD(x1,x0,x0);
|
||||
* Label(L0_); ADDI(x1,x1,1);
|
||||
* JAL(x0, LabelRef(L0_));
|
||||
*/
|
||||
|
||||
integer L0_, L1_, L2_, L3_, L4_, L5_, L6_, L7_;
|
||||
|
||||
task Label;
|
||||
output integer L;
|
||||
begin
|
||||
L = romPC;
|
||||
end
|
||||
endtask
|
||||
|
||||
function [31:0] LabelRef;
|
||||
input integer L;
|
||||
begin
|
||||
$display("romPC=%d",romPC);
|
||||
$display("L=%d",L);
|
||||
LabelRef = L - romPC;
|
||||
$display("LabelRef=%d",L - romPC);
|
||||
end
|
||||
endfunction
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
task SType;
|
||||
input [6:0] opcode;
|
||||
input [4:0] rs1;
|
||||
input [4:0] rs2;
|
||||
input [31:0] imm;
|
||||
input [2:0] funct3;
|
||||
begin
|
||||
ROM[romPC[31:2]] = {imm[11:5], rs2, rs1, funct3, imm[4:0], opcode};
|
||||
romPC = romPC + 4;
|
||||
end
|
||||
endtask
|
||||
|
||||
3
FemtoRV/TUTORIALS/FROM_BLINKER_TO_RISCV/run.sh
Executable file
3
FemtoRV/TUTORIALS/FROM_BLINKER_TO_RISCV/run.sh
Executable file
@@ -0,0 +1,3 @@
|
||||
rm -f a.out
|
||||
iverilog $1
|
||||
vvp a.out
|
||||
43
FemtoRV/TUTORIALS/FROM_BLINKER_TO_RISCV/step1.v
Normal file
43
FemtoRV/TUTORIALS/FROM_BLINKER_TO_RISCV/step1.v
Normal file
@@ -0,0 +1,43 @@
|
||||
/**
|
||||
* Step 1: simulation of a Blinker
|
||||
* Usage:
|
||||
* iverilog step1.v
|
||||
* vvp a.out
|
||||
* to exit: <ctrl><c> then finish
|
||||
*/
|
||||
|
||||
`default_nettype none
|
||||
|
||||
module Blink (
|
||||
input clock,
|
||||
output led
|
||||
);
|
||||
reg count;
|
||||
initial begin
|
||||
count = 0;
|
||||
end
|
||||
always @(posedge clock) begin
|
||||
count <= ~count;
|
||||
end
|
||||
assign led = count;
|
||||
endmodule
|
||||
|
||||
module bench();
|
||||
reg clock;
|
||||
wire led;
|
||||
|
||||
Blink uut(
|
||||
.clock(clock),
|
||||
.led(led)
|
||||
);
|
||||
|
||||
initial begin
|
||||
clock = 0;
|
||||
forever begin
|
||||
#1 clock = ~clock;
|
||||
$display("LED = %b",led);
|
||||
end
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
||||
219
FemtoRV/TUTORIALS/FROM_BLINKER_TO_RISCV/step10.v
Normal file
219
FemtoRV/TUTORIALS/FROM_BLINKER_TO_RISCV/step10.v
Normal file
@@ -0,0 +1,219 @@
|
||||
/**
|
||||
* Step 10: Creating a RISC-V processor
|
||||
* LUI and AUIPC
|
||||
* Usage:
|
||||
* iverilog step10.v
|
||||
* vvp a.out
|
||||
* to exit: <ctrl><c> then finish
|
||||
*/
|
||||
|
||||
`default_nettype none
|
||||
|
||||
module RiscV (
|
||||
input clock,
|
||||
output [4:0] leds
|
||||
);
|
||||
assign leds = 0; // we will use the LEDs later
|
||||
|
||||
reg [31:0] ROM [0:255];
|
||||
reg [31:0] PC; // program counter
|
||||
reg [31:0] instr; // current instruction
|
||||
|
||||
|
||||
`include "riscv_assembly.v"
|
||||
|
||||
// Initial value of program counter and instruction
|
||||
// register.
|
||||
initial begin
|
||||
PC = 0;
|
||||
instr = NOP_CODEOP;
|
||||
end
|
||||
|
||||
// ROM initialization, using our poor's men assembly
|
||||
// in "risc_assembly.v".
|
||||
initial begin
|
||||
LUI(x1, 32'b11111111111111111111111111111111); // Just takes the 20 MSBs (12 LSBs ignored)
|
||||
ORI(x1, x1, 32'b11111111111111111111111111111111); // Sets the 12 LSBs (20 MSBs ignored)
|
||||
EBREAK();
|
||||
end
|
||||
|
||||
|
||||
// See the table P. 105 in RISC-V manual
|
||||
// The 10 RISC-V instructions
|
||||
// Funny: what we do here is in fact just the reverse
|
||||
// of what's done in riscv_assembly.v !
|
||||
|
||||
wire isALUreg = (instr[6:0] == 7'b0110011); // rd <- rs1 OP rs2
|
||||
wire isALUimm = (instr[6:0] == 7'b0010011); // rd <- rs1 OP Iimm
|
||||
wire isBranch = (instr[6:0] == 7'b1100011); // if(rs1 OP rs2) PC<-PC+Bimm
|
||||
wire isJALR = (instr[6:0] == 7'b1100111); // rd <- PC+4; PC<-rs1+Iimm
|
||||
wire isJAL = (instr[6:0] == 7'b1101111); // rd <- PC+4; PC<-PC+Jimm
|
||||
wire isAUIPC = (instr[6:0] == 7'b0010111); // rd <- PC + Uimm
|
||||
wire isLUI = (instr[6:0] == 7'b0110111); // rd <- Uimm
|
||||
wire isLoad = (instr[6:0] == 7'b0000011); // rd <- mem[rs1+Iimm]
|
||||
wire isStore = (instr[6:0] == 7'b0100011); // mem[rs1+Simm] <- rs2
|
||||
wire isSYSTEM = (instr[6:0] == 7'b1110011); // special
|
||||
|
||||
// The 5 immediate formats
|
||||
wire [31:0] Uimm={ instr[31], instr[30:12], {12{1'b0}}};
|
||||
wire [31:0] Iimm={{21{instr[31]}}, instr[30:20]};
|
||||
wire [31:0] Simm={{21{instr[31]}}, instr[30:25],instr[11:7]};
|
||||
wire [31:0] Bimm={{20{instr[31]}}, instr[7],instr[30:25],instr[11:8],1'b0};
|
||||
wire [31:0] Jimm={{12{instr[31]}}, instr[19:12],instr[20],instr[30:21],1'b0};
|
||||
|
||||
// Source and destination registers
|
||||
wire [4:0] rs1Id = instr[19:15];
|
||||
wire [4:0] rs2Id = instr[24:20];
|
||||
wire [4:0] rdId = instr[11:7];
|
||||
|
||||
// function codes
|
||||
wire [2:0] funct3 = instr[14:12];
|
||||
wire [6:0] funct7 = instr[31:25];
|
||||
|
||||
// The registers bank
|
||||
reg [31:0] RegisterBank [0:31];
|
||||
reg [31:0] rs1; // value of source
|
||||
reg [31:0] rs2; // registers.
|
||||
wire [31:0] writeBackData; // data to be written to rd
|
||||
wire writeBackEn; // asserted if data should be written to rd
|
||||
|
||||
integer i;
|
||||
initial begin
|
||||
for(i=0; i<32; ++i) begin
|
||||
RegisterBank[i] = 0;
|
||||
end
|
||||
end
|
||||
|
||||
// The ALU
|
||||
wire [31:0] aluIn1 = rs1;
|
||||
wire [31:0] aluIn2 = isALUreg ? rs2 : Iimm;
|
||||
reg [31:0] aluOut;
|
||||
wire [4:0] shamt = isALUreg ? rs2[4:0] : instr[24:20]; // shift amount
|
||||
|
||||
always @(*) begin
|
||||
case(funct3)
|
||||
3'b000: aluOut = (funct7[5] & instr[5]) ? (aluIn1 - aluIn2) : (aluIn1 + aluIn2);
|
||||
3'b001: aluOut = aluIn1 << shamt;
|
||||
3'b010: aluOut = ($signed(aluIn1) < $signed(aluIn2));
|
||||
3'b011: aluOut = (aluIn1 < aluIn2);
|
||||
3'b100: aluOut = (aluIn1 ^ aluIn2);
|
||||
3'b101: aluOut = funct7[5]? ($signed(aluIn1) >>> shamt) : ($signed(aluIn1) >> shamt);
|
||||
3'b110: aluOut = (aluIn1 | aluIn2);
|
||||
3'b111: aluOut = (aluIn1 & aluIn2);
|
||||
endcase
|
||||
end
|
||||
|
||||
// ADD/SUB/ADDI:
|
||||
// funct7[5] is 1 for SUB and 0 for ADD. We need also to test instr[5]
|
||||
// to make the difference with ADDI
|
||||
//
|
||||
// SRLI/SRAI/SRL/SRA:
|
||||
// funct7[5] is 1 for arithmetic shift (SRA/SRAI) and 0 for logical shift (SRL/SRLI)
|
||||
|
||||
reg takeBranch;
|
||||
always @(*) begin
|
||||
case(funct3)
|
||||
3'b000: takeBranch = (rs1 == rs2);
|
||||
3'b001: takeBranch = (rs1 != rs2);
|
||||
3'b100: takeBranch = ($signed(rs1) < $signed(rs2));
|
||||
3'b101: takeBranch = ($signed(rs1) >= $signed(rs2));
|
||||
3'b110: takeBranch = (rs1 < rs2);
|
||||
3'b110: takeBranch = (rs1 >= rs2);
|
||||
default: takeBranch = 1'b0;
|
||||
endcase
|
||||
end
|
||||
|
||||
// The state machine
|
||||
localparam FETCH_INSTR = 0;
|
||||
localparam FETCH_REGS = 1;
|
||||
localparam EXECUTE = 2;
|
||||
reg [1:0] state;
|
||||
|
||||
// register write back
|
||||
assign writeBackData =
|
||||
(isJAL || isJALR) ? (PC + 4) :
|
||||
(isLUI) ? Uimm :
|
||||
(isAUIPC) ? (PC + Uimm) :
|
||||
aluOut;
|
||||
assign writeBackEn = (state == EXECUTE && (isALUreg || isALUimm || isJAL || isJALR || isLUI || isAUIPC));
|
||||
|
||||
// next PC
|
||||
wire [31:0] nextPC =
|
||||
(isBranch && takeBranch) ? PC+Bimm :
|
||||
isJAL ? PC+Jimm :
|
||||
isJALR ? rs1+Iimm :
|
||||
PC+4;
|
||||
|
||||
initial begin
|
||||
state = FETCH_INSTR;
|
||||
end
|
||||
|
||||
always @(posedge clock) begin
|
||||
case(state)
|
||||
FETCH_INSTR: begin
|
||||
instr <= ROM[PC[31:2]];
|
||||
state <= FETCH_REGS;
|
||||
end
|
||||
FETCH_REGS: begin
|
||||
rs1 <= RegisterBank[rs1Id];
|
||||
rs2 <= RegisterBank[rs2Id];
|
||||
state <= EXECUTE;
|
||||
end
|
||||
EXECUTE: begin
|
||||
case (1'b1)
|
||||
isALUreg: $display(
|
||||
"ALUreg rd=%d rs1=%d rs2=%d funct3=%b",
|
||||
rdId, rs1Id, rs2Id, funct3
|
||||
);
|
||||
isALUimm: $display(
|
||||
"ALUimm rd=%d rs1=%d imm=%0d funct3=%b",
|
||||
rdId, rs1Id, Iimm, funct3
|
||||
);
|
||||
isBranch: $display(
|
||||
"BRANCH rs1=%d rs2=%d takeBranch=%b",
|
||||
rs1Id, rs2Id, takeBranch
|
||||
);
|
||||
isJAL: $display("JAL");
|
||||
isJALR: $display("JALR");
|
||||
isAUIPC: $display("AUIPC");
|
||||
isLUI: $display("LUI");
|
||||
isLoad: $display("LOAD");
|
||||
isStore: $display("STORE");
|
||||
isSYSTEM: $display("SYSTEM");
|
||||
endcase
|
||||
if(isSYSTEM) begin
|
||||
$finish();
|
||||
end
|
||||
PC <= nextPC;
|
||||
state <= FETCH_INSTR;
|
||||
end
|
||||
endcase
|
||||
|
||||
if(writeBackEn && rdId != 0) begin
|
||||
RegisterBank[rdId] <= writeBackData;
|
||||
$display("x%0d <= %b",rdId,writeBackData);
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
endmodule
|
||||
|
||||
module bench();
|
||||
reg clock;
|
||||
wire [4:0] leds;
|
||||
|
||||
RiscV uut(
|
||||
.clock(clock),
|
||||
.leds(leds)
|
||||
);
|
||||
|
||||
initial begin
|
||||
clock = 0;
|
||||
forever begin
|
||||
#1 clock = ~clock;
|
||||
// $display("LEDS=%b",leds); // we will use the LEDs later
|
||||
end
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
||||
44
FemtoRV/TUTORIALS/FROM_BLINKER_TO_RISCV/step2.v
Normal file
44
FemtoRV/TUTORIALS/FROM_BLINKER_TO_RISCV/step2.v
Normal file
@@ -0,0 +1,44 @@
|
||||
/**
|
||||
* Step 2: simulation of a Blinker
|
||||
* version with five LEDs
|
||||
* Usage:
|
||||
* iverilog step1.v
|
||||
* vvp a.out
|
||||
* to exit: <ctrl><c> then finish
|
||||
*/
|
||||
|
||||
`default_nettype none
|
||||
|
||||
module Blink (
|
||||
input clock,
|
||||
output [4:0] leds
|
||||
);
|
||||
reg [4:0] count;
|
||||
initial begin
|
||||
count = 0;
|
||||
end
|
||||
always @(posedge clock) begin
|
||||
count <= count + 1;
|
||||
end
|
||||
assign leds = count;
|
||||
endmodule
|
||||
|
||||
module bench();
|
||||
reg clock;
|
||||
wire [4:0] leds;
|
||||
|
||||
Blink uut(
|
||||
.clock(clock),
|
||||
.leds(leds)
|
||||
);
|
||||
|
||||
initial begin
|
||||
clock = 0;
|
||||
forever begin
|
||||
#1 clock = ~clock;
|
||||
$display("LEDS=%b",leds);
|
||||
end
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
||||
45
FemtoRV/TUTORIALS/FROM_BLINKER_TO_RISCV/step3.v
Normal file
45
FemtoRV/TUTORIALS/FROM_BLINKER_TO_RISCV/step3.v
Normal file
@@ -0,0 +1,45 @@
|
||||
/**
|
||||
* Step 3: simulation of a Blinker
|
||||
* version with five LEDs
|
||||
* slower version
|
||||
* Usage:
|
||||
* iverilog step1.v
|
||||
* vvp a.out
|
||||
* to exit: <ctrl><c> then finish
|
||||
*/
|
||||
|
||||
`default_nettype none
|
||||
|
||||
module Blink (
|
||||
input clock,
|
||||
output [4:0] leds
|
||||
);
|
||||
reg [21:0] count;
|
||||
initial begin
|
||||
count = 0;
|
||||
end
|
||||
always @(posedge clock) begin
|
||||
count <= count + 1;
|
||||
end
|
||||
assign leds = count[21:17];
|
||||
endmodule
|
||||
|
||||
module bench();
|
||||
reg clock;
|
||||
wire [4:0] leds;
|
||||
|
||||
Blink uut(
|
||||
.clock(clock),
|
||||
.leds(leds)
|
||||
);
|
||||
|
||||
initial begin
|
||||
clock = 0;
|
||||
forever begin
|
||||
#1 clock = ~clock;
|
||||
$display("LEDS=%b",leds);
|
||||
end
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
||||
129
FemtoRV/TUTORIALS/FROM_BLINKER_TO_RISCV/step4.v
Normal file
129
FemtoRV/TUTORIALS/FROM_BLINKER_TO_RISCV/step4.v
Normal file
@@ -0,0 +1,129 @@
|
||||
/**
|
||||
* Step 4: Creating a RISC-V processor
|
||||
* The instruction decoder
|
||||
* Usage:
|
||||
* iverilog step1.v
|
||||
* vvp a.out
|
||||
* to exit: <ctrl><c> then finish
|
||||
*/
|
||||
|
||||
`default_nettype none
|
||||
|
||||
module RiscV (
|
||||
input clock,
|
||||
output [4:0] leds
|
||||
);
|
||||
assign leds = 0; // we will use the LEDs later
|
||||
|
||||
reg [31:0] ROM [0:255];
|
||||
reg [31:0] PC; // program counter
|
||||
reg [31:0] instr; // current instruction
|
||||
|
||||
initial begin
|
||||
|
||||
PC = 0;
|
||||
|
||||
// add x0, x0, x0
|
||||
// rs2 rs1 add rd ALUREG
|
||||
instr = 32'b0000000_00000_00000_000_00000_0110011;
|
||||
// add x1, x0, x0
|
||||
// rs2 rs1 add rd ALUREG
|
||||
ROM[0] = 32'b0000000_00000_00000_000_00001_0110011;
|
||||
// addi x1, x1, 1
|
||||
// imm rs1 add rd ALUIMM
|
||||
ROM[1] = 32'b000000000001_00001_000_00001_0010011;
|
||||
// addi x1, x1, 1
|
||||
// imm rs1 add rd ALUIMM
|
||||
ROM[2] = 32'b000000000001_00001_000_00001_0010011;
|
||||
// addi x1, x1, 1
|
||||
// imm rs1 add rd ALUIMM
|
||||
ROM[3] = 32'b000000000001_00001_000_00001_0010011;
|
||||
// addi x1, x1, 1
|
||||
// imm rs1 add rd ALUIMM
|
||||
ROM[4] = 32'b000000000001_00001_000_00001_0010011;
|
||||
|
||||
|
||||
ROM[5] = 0;
|
||||
|
||||
end
|
||||
|
||||
always @(posedge clock) begin
|
||||
instr <= ROM[PC];
|
||||
PC <= PC + 1;
|
||||
if(instr == 0) begin
|
||||
$finish();
|
||||
end
|
||||
end
|
||||
|
||||
// See the table P. 105 in RISC-V manual
|
||||
|
||||
// The 10 RISC-V instructions
|
||||
wire isALUreg = (instr[6:0] == 7'b0110011); // rd <- rs1 OP rs2
|
||||
wire isALUimm = (instr[6:0] == 7'b0010011); // rd <- rs1 OP Iimm
|
||||
wire isBranch = (instr[6:0] == 7'b1100011); // if(rs1 OP rs2) PC<-PC+Bimm
|
||||
wire isJALR = (instr[6:0] == 7'b1100111); // rd <- PC+4; PC<-rs1+Iimm
|
||||
wire isJAL = (instr[6:0] == 7'b1101111); // rd <- PC+4; PC<-PC+Jimm
|
||||
wire isAUIPC = (instr[6:0] == 7'b0010111); // rd <- PC + Uimm
|
||||
wire isLUI = (instr[6:0] == 7'b0110111); // rd <- Uimm
|
||||
wire isLoad = (instr[6:0] == 7'b0000011); // rd <- mem[rs1+Iimm]
|
||||
wire isStore = (instr[6:0] == 7'b0100011); // mem[rs1+Simm] <- rs2
|
||||
wire isSYSTEM = (instr[6:0] == 7'b1110011); // special
|
||||
|
||||
// The 5 immediate formats
|
||||
wire [31:0] Uimm={ instr[31], instr[30:12], {12{1'b0}}};
|
||||
wire [31:0] Iimm={{21{instr[31]}}, instr[30:20]};
|
||||
wire [31:0] Simm={{21{instr[31]}}, instr[30:25],instr[11:7]};
|
||||
wire [31:0] Bimm={{20{instr[31]}}, instr[7],instr[30:25],instr[11:8],1'b0};
|
||||
wire [31:0] Jimm={{12{instr[31]}}, instr[19:12],instr[20],instr[30:21],1'b0};
|
||||
|
||||
// Source and destination registers
|
||||
wire [4:0] rs1Id = instr[19:15];
|
||||
wire [4:0] rs2Id = instr[24:20];
|
||||
wire [4:0] rdId = instr[11:7];
|
||||
|
||||
// function codes
|
||||
wire [2:0] funct3 = instr[14:12];
|
||||
wire [6:0] funct7 = instr[31:25];
|
||||
|
||||
always @(posedge clock) begin
|
||||
case (1'b1)
|
||||
isALUreg: $display(
|
||||
"ALUreg rd=%d rs1=%d rs2=%d funct3=%b",
|
||||
rdId, rs1Id, rs2Id, funct3
|
||||
);
|
||||
isALUimm: $display(
|
||||
"ALUimm rd=%d rs1=%d imm=%0d funct3=%b",
|
||||
rdId, rs1Id, Iimm, funct3
|
||||
);
|
||||
isBranch: $display("BRANCH");
|
||||
isJAL: $display("JAL");
|
||||
isJALR: $display("JALR");
|
||||
isAUIPC: $display("AUIPC");
|
||||
isLUI: $display("LUI");
|
||||
isLoad: $display("LOAD");
|
||||
isStore: $display("STORE");
|
||||
isSYSTEM: $display("SYSTEM");
|
||||
endcase
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
||||
module bench();
|
||||
reg clock;
|
||||
wire [4:0] leds;
|
||||
|
||||
RiscV uut(
|
||||
.clock(clock),
|
||||
.leds(leds)
|
||||
);
|
||||
|
||||
initial begin
|
||||
clock = 0;
|
||||
forever begin
|
||||
#1 clock = ~clock;
|
||||
// $display("LEDS=%b",leds); // we will use the LEDs later
|
||||
end
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
||||
169
FemtoRV/TUTORIALS/FROM_BLINKER_TO_RISCV/step5.v
Normal file
169
FemtoRV/TUTORIALS/FROM_BLINKER_TO_RISCV/step5.v
Normal file
@@ -0,0 +1,169 @@
|
||||
/**
|
||||
* Step 5: Creating a RISC-V processor
|
||||
* The register bank and the state machine
|
||||
* Usage:
|
||||
* iverilog step1.v
|
||||
* vvp a.out
|
||||
* to exit: <ctrl><c> then finish
|
||||
*/
|
||||
|
||||
`default_nettype none
|
||||
|
||||
module RiscV (
|
||||
input clock,
|
||||
output [4:0] leds
|
||||
);
|
||||
assign leds = 0; // we will use the LEDs later
|
||||
|
||||
reg [31:0] ROM [0:255];
|
||||
reg [31:0] PC; // program counter
|
||||
reg [31:0] instr; // current instruction
|
||||
|
||||
initial begin
|
||||
PC = 0;
|
||||
|
||||
// add x0, x0, x0
|
||||
// rs2 rs1 add rd ALUREG
|
||||
instr = 32'b0000000_00000_00000_000_00000_0110011;
|
||||
// add x1, x0, x0
|
||||
// rs2 rs1 add rd ALUREG
|
||||
ROM[0] = 32'b0000000_00000_00000_000_00001_0110011;
|
||||
// addi x1, x1, 1
|
||||
// imm rs1 add rd ALUIMM
|
||||
ROM[1] = 32'b000000000001_00001_000_00001_0010011;
|
||||
// addi x1, x1, 1
|
||||
// imm rs1 add rd ALUIMM
|
||||
ROM[2] = 32'b000000000001_00001_000_00001_0010011;
|
||||
// addi x1, x1, 1
|
||||
// imm rs1 add rd ALUIMM
|
||||
ROM[3] = 32'b000000000001_00001_000_00001_0010011;
|
||||
// addi x1, x1, 1
|
||||
// imm rs1 add rd ALUIMM
|
||||
ROM[4] = 32'b000000000001_00001_000_00001_0010011;
|
||||
|
||||
|
||||
ROM[5] = 0;
|
||||
|
||||
end
|
||||
|
||||
|
||||
// See the table P. 105 in RISC-V manual
|
||||
|
||||
// The 10 RISC-V instructions
|
||||
wire isALUreg = (instr[6:0] == 7'b0110011); // rd <- rs1 OP rs2
|
||||
wire isALUimm = (instr[6:0] == 7'b0010011); // rd <- rs1 OP Iimm
|
||||
wire isBranch = (instr[6:0] == 7'b1100011); // if(rs1 OP rs2) PC<-PC+Bimm
|
||||
wire isJALR = (instr[6:0] == 7'b1100111); // rd <- PC+4; PC<-rs1+Iimm
|
||||
wire isJAL = (instr[6:0] == 7'b1101111); // rd <- PC+4; PC<-PC+Jimm
|
||||
wire isAUIPC = (instr[6:0] == 7'b0010111); // rd <- PC + Uimm
|
||||
wire isLUI = (instr[6:0] == 7'b0110111); // rd <- Uimm
|
||||
wire isLoad = (instr[6:0] == 7'b0000011); // rd <- mem[rs1+Iimm]
|
||||
wire isStore = (instr[6:0] == 7'b0100011); // mem[rs1+Simm] <- rs2
|
||||
wire isSYSTEM = (instr[6:0] == 7'b1110011); // special
|
||||
|
||||
// The 5 immediate formats
|
||||
wire [31:0] Uimm={ instr[31], instr[30:12], {12{1'b0}}};
|
||||
wire [31:0] Iimm={{21{instr[31]}}, instr[30:20]};
|
||||
wire [31:0] Simm={{21{instr[31]}}, instr[30:25],instr[11:7]};
|
||||
wire [31:0] Bimm={{20{instr[31]}}, instr[7],instr[30:25],instr[11:8],1'b0};
|
||||
wire [31:0] Jimm={{12{instr[31]}}, instr[19:12],instr[20],instr[30:21],1'b0};
|
||||
|
||||
// Source and destination registers
|
||||
wire [4:0] rs1Id = instr[19:15];
|
||||
wire [4:0] rs2Id = instr[24:20];
|
||||
wire [4:0] rdId = instr[11:7];
|
||||
|
||||
// function codes
|
||||
wire [2:0] funct3 = instr[14:12];
|
||||
wire [6:0] funct7 = instr[31:25];
|
||||
|
||||
// The registers bank
|
||||
reg [31:0] RegisterBank [0:31];
|
||||
reg [31:0] rs1;
|
||||
reg [31:0] rs2;
|
||||
wire [31:0] writeBackData;
|
||||
wire writeBackEn;
|
||||
assign writeBackData = 0; // for now
|
||||
assign writeBackEn = 0; // for now
|
||||
|
||||
integer i;
|
||||
initial begin
|
||||
for(i=0; i<32; ++i) begin
|
||||
RegisterBank[i] = 0;
|
||||
end
|
||||
end
|
||||
|
||||
// The state machine
|
||||
|
||||
localparam FETCH_INSTR = 0;
|
||||
localparam FETCH_REGS = 1;
|
||||
localparam EXECUTE = 2;
|
||||
reg [1:0] state;
|
||||
|
||||
initial begin
|
||||
state = FETCH_INSTR;
|
||||
end
|
||||
|
||||
always @(posedge clock) begin
|
||||
if(writeBackEn && rdId != 0) begin
|
||||
RegisterBank[rdId] <= writeBackData;
|
||||
end
|
||||
|
||||
case(state)
|
||||
FETCH_INSTR: begin
|
||||
instr <= ROM[PC];
|
||||
state <= FETCH_REGS;
|
||||
end
|
||||
FETCH_REGS: begin
|
||||
rs1 <= RegisterBank[rs1Id];
|
||||
rs2 <= RegisterBank[rs2Id];
|
||||
state <= EXECUTE;
|
||||
end
|
||||
EXECUTE: begin
|
||||
case (1'b1)
|
||||
isALUreg: $display(
|
||||
"ALUreg rd=%d rs1=%d rs2=%d funct3=%b",
|
||||
rdId, rs1Id, rs2Id, funct3
|
||||
);
|
||||
isALUimm: $display(
|
||||
"ALUimm rd=%d rs1=%d imm=%0d funct3=%b",
|
||||
rdId, rs1Id, Iimm, funct3
|
||||
);
|
||||
isBranch: $display("BRANCH");
|
||||
isJAL: $display("JAL");
|
||||
isJALR: $display("JALR");
|
||||
isAUIPC: $display("AUIPC");
|
||||
isLUI: $display("LUI");
|
||||
isLoad: $display("LOAD");
|
||||
isStore: $display("STORE");
|
||||
isSYSTEM: $display("SYSTEM");
|
||||
endcase
|
||||
if(instr == 0) begin
|
||||
$finish();
|
||||
end
|
||||
PC <= PC + 1;
|
||||
state <= FETCH_INSTR;
|
||||
end
|
||||
endcase
|
||||
end
|
||||
endmodule
|
||||
|
||||
module bench();
|
||||
reg clock;
|
||||
wire [4:0] leds;
|
||||
|
||||
RiscV uut(
|
||||
.clock(clock),
|
||||
.leds(leds)
|
||||
);
|
||||
|
||||
initial begin
|
||||
clock = 0;
|
||||
forever begin
|
||||
#1 clock = ~clock;
|
||||
// $display("LEDS=%b",leds); // we will use the LEDs later
|
||||
end
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
||||
212
FemtoRV/TUTORIALS/FROM_BLINKER_TO_RISCV/step6.v
Normal file
212
FemtoRV/TUTORIALS/FROM_BLINKER_TO_RISCV/step6.v
Normal file
@@ -0,0 +1,212 @@
|
||||
/**
|
||||
* Step 6: Creating a RISC-V processor
|
||||
* The ALU
|
||||
* Usage:
|
||||
* iverilog step1.v
|
||||
* vvp a.out
|
||||
* to exit: <ctrl><c> then finish
|
||||
*/
|
||||
|
||||
`default_nettype none
|
||||
|
||||
module RiscV (
|
||||
input clock,
|
||||
output [4:0] leds
|
||||
);
|
||||
assign leds = 0; // we will use the LEDs later
|
||||
|
||||
reg [31:0] ROM [0:255];
|
||||
reg [31:0] PC; // program counter
|
||||
reg [31:0] instr; // current instruction
|
||||
|
||||
initial begin
|
||||
PC = 0;
|
||||
|
||||
// add x0, x0, x0
|
||||
// rs2 rs1 add rd ALUREG
|
||||
instr = 32'b0000000_00000_00000_000_00000_0110011;
|
||||
// add x1, x0, x0
|
||||
// rs2 rs1 add rd ALUREG
|
||||
ROM[0] = 32'b0000000_00000_00000_000_00001_0110011;
|
||||
// addi x1, x1, 1
|
||||
// imm rs1 add rd ALUIMM
|
||||
ROM[1] = 32'b000000000001_00001_000_00001_0010011;
|
||||
// addi x1, x1, 1
|
||||
// imm rs1 add rd ALUIMM
|
||||
ROM[2] = 32'b000000000001_00001_000_00001_0010011;
|
||||
// addi x1, x1, 1
|
||||
// imm rs1 add rd ALUIMM
|
||||
ROM[3] = 32'b000000000001_00001_000_00001_0010011;
|
||||
// addi x1, x1, 1
|
||||
// imm rs1 add rd ALUIMM
|
||||
ROM[4] = 32'b000000000001_00001_000_00001_0010011;
|
||||
// add x2, x1, x0
|
||||
// rs2 rs1 add rd ALUREG
|
||||
ROM[5] = 32'b0000000_00000_00001_000_00010_0110011;
|
||||
// add x3, x1, x2
|
||||
// rs2 rs1 add rd ALUREG
|
||||
ROM[6] = 32'b0000000_00010_00001_000_00011_0110011;
|
||||
// srli x3, x3, 3
|
||||
// shamt rs1 sr rd ALUIMM
|
||||
ROM[7] = 32'b0000000_00011_00011_101_00011_0010011;
|
||||
// slli x3, x3, 31
|
||||
// shamt rs1 sl rd ALUIMM
|
||||
ROM[8] = 32'b0000000_11111_00011_001_00011_0010011;
|
||||
// srai x3, x3, 5
|
||||
// shamt rs1 sr rd ALUIMM
|
||||
ROM[9] = 32'b0100000_00101_00011_101_00011_0010011;
|
||||
|
||||
ROM[10] = 0;
|
||||
end
|
||||
|
||||
|
||||
// See the table P. 105 in RISC-V manual
|
||||
|
||||
// The 10 RISC-V instructions
|
||||
wire isALUreg = (instr[6:0] == 7'b0110011); // rd <- rs1 OP rs2
|
||||
wire isALUimm = (instr[6:0] == 7'b0010011); // rd <- rs1 OP Iimm
|
||||
wire isBranch = (instr[6:0] == 7'b1100011); // if(rs1 OP rs2) PC<-PC+Bimm
|
||||
wire isJALR = (instr[6:0] == 7'b1100111); // rd <- PC+4; PC<-rs1+Iimm
|
||||
wire isJAL = (instr[6:0] == 7'b1101111); // rd <- PC+4; PC<-PC+Jimm
|
||||
wire isAUIPC = (instr[6:0] == 7'b0010111); // rd <- PC + Uimm
|
||||
wire isLUI = (instr[6:0] == 7'b0110111); // rd <- Uimm
|
||||
wire isLoad = (instr[6:0] == 7'b0000011); // rd <- mem[rs1+Iimm]
|
||||
wire isStore = (instr[6:0] == 7'b0100011); // mem[rs1+Simm] <- rs2
|
||||
wire isSYSTEM = (instr[6:0] == 7'b1110011); // special
|
||||
|
||||
// The 5 immediate formats
|
||||
wire [31:0] Uimm={ instr[31], instr[30:12], {12{1'b0}}};
|
||||
wire [31:0] Iimm={{21{instr[31]}}, instr[30:20]};
|
||||
wire [31:0] Simm={{21{instr[31]}}, instr[30:25],instr[11:7]};
|
||||
wire [31:0] Bimm={{20{instr[31]}}, instr[7],instr[30:25],instr[11:8],1'b0};
|
||||
wire [31:0] Jimm={{12{instr[31]}}, instr[19:12],instr[20],instr[30:21],1'b0};
|
||||
|
||||
// Source and destination registers
|
||||
wire [4:0] rs1Id = instr[19:15];
|
||||
wire [4:0] rs2Id = instr[24:20];
|
||||
wire [4:0] rdId = instr[11:7];
|
||||
|
||||
// function codes
|
||||
wire [2:0] funct3 = instr[14:12];
|
||||
wire [6:0] funct7 = instr[31:25];
|
||||
|
||||
// The registers bank
|
||||
reg [31:0] RegisterBank [0:31];
|
||||
reg [31:0] rs1; // value of source
|
||||
reg [31:0] rs2; // registers.
|
||||
wire [31:0] writeBackData; // data to be written to rd
|
||||
wire writeBackEn; // asserted if data should be written to rd
|
||||
|
||||
integer i;
|
||||
initial begin
|
||||
for(i=0; i<32; ++i) begin
|
||||
RegisterBank[i] = 0;
|
||||
end
|
||||
end
|
||||
|
||||
// The ALU
|
||||
wire [31:0] aluIn1 = rs1;
|
||||
wire [31:0] aluIn2 = isALUreg ? rs2 : Iimm;
|
||||
reg [31:0] aluOut;
|
||||
wire [4:0] shamt = isALUreg ? rs2[4:0] : instr[24:20]; // shift amount
|
||||
|
||||
always @(*) begin
|
||||
case(funct3)
|
||||
3'b000: aluOut = (funct7[5] & instr[5]) ? (aluIn1 - aluIn2) : (aluIn1 + aluIn2);
|
||||
3'b001: aluOut = aluIn1 << shamt;
|
||||
3'b010: aluOut = ($signed(aluIn1) < $signed(aluIn2));
|
||||
3'b011: aluOut = (aluIn1 < aluIn2);
|
||||
3'b100: aluOut = (aluIn1 ^ aluIn2);
|
||||
3'b101: aluOut = funct7[5]? ($signed(aluIn1) >>> shamt) : ($signed(aluIn1) >> shamt);
|
||||
3'b110: aluOut = (aluIn1 | aluIn2);
|
||||
3'b111: aluOut = (aluIn1 & aluIn2);
|
||||
endcase
|
||||
end
|
||||
|
||||
// ADD/SUB/ADDI:
|
||||
// funct7[5] is 1 for SUB and 0 for ADD. We need also to test instr[5]
|
||||
// to make the difference with ADDI
|
||||
//
|
||||
// SRLI/SRAI/SRL/SRA:
|
||||
// funct7[5] is 1 for arithmetic shift (SRA/SRAI) and 0 for logical shift (SRL/SRLI)
|
||||
|
||||
// The state machine
|
||||
localparam FETCH_INSTR = 0;
|
||||
localparam FETCH_REGS = 1;
|
||||
localparam EXECUTE = 2;
|
||||
reg [1:0] state;
|
||||
|
||||
// register write back
|
||||
assign writeBackData = aluOut;
|
||||
assign writeBackEn = (state == EXECUTE && (isALUreg || isALUimm));
|
||||
|
||||
initial begin
|
||||
state = FETCH_INSTR;
|
||||
end
|
||||
|
||||
always @(posedge clock) begin
|
||||
case(state)
|
||||
FETCH_INSTR: begin
|
||||
instr <= ROM[PC];
|
||||
state <= FETCH_REGS;
|
||||
end
|
||||
FETCH_REGS: begin
|
||||
rs1 <= RegisterBank[rs1Id];
|
||||
rs2 <= RegisterBank[rs2Id];
|
||||
state <= EXECUTE;
|
||||
end
|
||||
EXECUTE: begin
|
||||
case (1'b1)
|
||||
isALUreg: $display(
|
||||
"ALUreg rd=%d rs1=%d rs2=%d funct3=%b",
|
||||
rdId, rs1Id, rs2Id, funct3
|
||||
);
|
||||
isALUimm: $display(
|
||||
"ALUimm rd=%d rs1=%d imm=%0d funct3=%b",
|
||||
rdId, rs1Id, Iimm, funct3
|
||||
);
|
||||
isBranch: $display("BRANCH");
|
||||
isJAL: $display("JAL");
|
||||
isJALR: $display("JALR");
|
||||
isAUIPC: $display("AUIPC");
|
||||
isLUI: $display("LUI");
|
||||
isLoad: $display("LOAD");
|
||||
isStore: $display("STORE");
|
||||
isSYSTEM: $display("SYSTEM");
|
||||
endcase
|
||||
if(instr == 0) begin
|
||||
$finish();
|
||||
end
|
||||
PC <= PC + 1;
|
||||
state <= FETCH_INSTR;
|
||||
end
|
||||
endcase
|
||||
|
||||
if(writeBackEn && rdId != 0) begin
|
||||
RegisterBank[rdId] <= writeBackData;
|
||||
$display("x%0d <= %b",rdId,writeBackData);
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
endmodule
|
||||
|
||||
module bench();
|
||||
reg clock;
|
||||
wire [4:0] leds;
|
||||
|
||||
RiscV uut(
|
||||
.clock(clock),
|
||||
.leds(leds)
|
||||
);
|
||||
|
||||
initial begin
|
||||
clock = 0;
|
||||
forever begin
|
||||
#1 clock = ~clock;
|
||||
// $display("LEDS=%b",leds); // we will use the LEDs later
|
||||
end
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
||||
201
FemtoRV/TUTORIALS/FROM_BLINKER_TO_RISCV/step7.v
Normal file
201
FemtoRV/TUTORIALS/FROM_BLINKER_TO_RISCV/step7.v
Normal file
@@ -0,0 +1,201 @@
|
||||
/**
|
||||
* Step 7: Creating a RISC-V processor
|
||||
* Assembly
|
||||
* Usage:
|
||||
* iverilog step1.v
|
||||
* vvp a.out
|
||||
* to exit: <ctrl><c> then finish
|
||||
*/
|
||||
|
||||
`default_nettype none
|
||||
|
||||
module RiscV (
|
||||
input clock,
|
||||
output [4:0] leds
|
||||
);
|
||||
assign leds = 0; // we will use the LEDs later
|
||||
|
||||
reg [31:0] ROM [0:255];
|
||||
reg [31:0] PC; // program counter
|
||||
reg [31:0] instr; // current instruction
|
||||
|
||||
|
||||
`include "riscv_assembly.v"
|
||||
|
||||
// Initial value of program counter and instruction
|
||||
// register.
|
||||
initial begin
|
||||
PC = 0;
|
||||
instr = NOP_CODEOP;
|
||||
end
|
||||
|
||||
// ROM initialization, using our poor's men assembly
|
||||
// in "risc_assembly.v".
|
||||
initial begin
|
||||
ADD(x0,x0,x0);
|
||||
ADD(x1,x0,x0);
|
||||
ADDI(x1,x1,1);
|
||||
ADDI(x1,x1,1);
|
||||
ADDI(x1,x1,1);
|
||||
ADDI(x1,x1,1);
|
||||
ADD(x2,x1,x0);
|
||||
ADD(x3,x1,x2);
|
||||
SRLI(x3,x3,3);
|
||||
SLLI(x3,x3,31);
|
||||
SRAI(x3,x3,5);
|
||||
EBREAK();
|
||||
end
|
||||
|
||||
|
||||
// See the table P. 105 in RISC-V manual
|
||||
// The 10 RISC-V instructions
|
||||
// Funny: what we do here is in fact just the reverse
|
||||
// of what's done in riscv_assembly.v !
|
||||
|
||||
wire isALUreg = (instr[6:0] == 7'b0110011); // rd <- rs1 OP rs2
|
||||
wire isALUimm = (instr[6:0] == 7'b0010011); // rd <- rs1 OP Iimm
|
||||
wire isBranch = (instr[6:0] == 7'b1100011); // if(rs1 OP rs2) PC<-PC+Bimm
|
||||
wire isJALR = (instr[6:0] == 7'b1100111); // rd <- PC+4; PC<-rs1+Iimm
|
||||
wire isJAL = (instr[6:0] == 7'b1101111); // rd <- PC+4; PC<-PC+Jimm
|
||||
wire isAUIPC = (instr[6:0] == 7'b0010111); // rd <- PC + Uimm
|
||||
wire isLUI = (instr[6:0] == 7'b0110111); // rd <- Uimm
|
||||
wire isLoad = (instr[6:0] == 7'b0000011); // rd <- mem[rs1+Iimm]
|
||||
wire isStore = (instr[6:0] == 7'b0100011); // mem[rs1+Simm] <- rs2
|
||||
wire isSYSTEM = (instr[6:0] == 7'b1110011); // special
|
||||
|
||||
// The 5 immediate formats
|
||||
wire [31:0] Uimm={ instr[31], instr[30:12], {12{1'b0}}};
|
||||
wire [31:0] Iimm={{21{instr[31]}}, instr[30:20]};
|
||||
wire [31:0] Simm={{21{instr[31]}}, instr[30:25],instr[11:7]};
|
||||
wire [31:0] Bimm={{20{instr[31]}}, instr[7],instr[30:25],instr[11:8],1'b0};
|
||||
wire [31:0] Jimm={{12{instr[31]}}, instr[19:12],instr[20],instr[30:21],1'b0};
|
||||
|
||||
// Source and destination registers
|
||||
wire [4:0] rs1Id = instr[19:15];
|
||||
wire [4:0] rs2Id = instr[24:20];
|
||||
wire [4:0] rdId = instr[11:7];
|
||||
|
||||
// function codes
|
||||
wire [2:0] funct3 = instr[14:12];
|
||||
wire [6:0] funct7 = instr[31:25];
|
||||
|
||||
// The registers bank
|
||||
reg [31:0] RegisterBank [0:31];
|
||||
reg [31:0] rs1; // value of source
|
||||
reg [31:0] rs2; // registers.
|
||||
wire [31:0] writeBackData; // data to be written to rd
|
||||
wire writeBackEn; // asserted if data should be written to rd
|
||||
|
||||
integer i;
|
||||
initial begin
|
||||
for(i=0; i<32; ++i) begin
|
||||
RegisterBank[i] = 0;
|
||||
end
|
||||
end
|
||||
|
||||
// The ALU
|
||||
wire [31:0] aluIn1 = rs1;
|
||||
wire [31:0] aluIn2 = isALUreg ? rs2 : Iimm;
|
||||
reg [31:0] aluOut;
|
||||
wire [4:0] shamt = isALUreg ? rs2[4:0] : instr[24:20]; // shift amount
|
||||
|
||||
always @(*) begin
|
||||
case(funct3)
|
||||
3'b000: aluOut = (funct7[5] & instr[5]) ? (aluIn1 - aluIn2) : (aluIn1 + aluIn2);
|
||||
3'b001: aluOut = aluIn1 << shamt;
|
||||
3'b010: aluOut = ($signed(aluIn1) < $signed(aluIn2));
|
||||
3'b011: aluOut = (aluIn1 < aluIn2);
|
||||
3'b100: aluOut = (aluIn1 ^ aluIn2);
|
||||
3'b101: aluOut = funct7[5]? ($signed(aluIn1) >>> shamt) : ($signed(aluIn1) >> shamt);
|
||||
3'b110: aluOut = (aluIn1 | aluIn2);
|
||||
3'b111: aluOut = (aluIn1 & aluIn2);
|
||||
endcase
|
||||
end
|
||||
|
||||
// ADD/SUB/ADDI:
|
||||
// funct7[5] is 1 for SUB and 0 for ADD. We need also to test instr[5]
|
||||
// to make the difference with ADDI
|
||||
//
|
||||
// SRLI/SRAI/SRL/SRA:
|
||||
// funct7[5] is 1 for arithmetic shift (SRA/SRAI) and 0 for logical shift (SRL/SRLI)
|
||||
|
||||
// The state machine
|
||||
localparam FETCH_INSTR = 0;
|
||||
localparam FETCH_REGS = 1;
|
||||
localparam EXECUTE = 2;
|
||||
reg [1:0] state;
|
||||
|
||||
// register write back
|
||||
assign writeBackData = aluOut;
|
||||
assign writeBackEn = (state == EXECUTE && (isALUreg || isALUimm));
|
||||
|
||||
initial begin
|
||||
state = FETCH_INSTR;
|
||||
end
|
||||
|
||||
always @(posedge clock) begin
|
||||
case(state)
|
||||
FETCH_INSTR: begin
|
||||
instr <= ROM[PC];
|
||||
state <= FETCH_REGS;
|
||||
end
|
||||
FETCH_REGS: begin
|
||||
rs1 <= RegisterBank[rs1Id];
|
||||
rs2 <= RegisterBank[rs2Id];
|
||||
state <= EXECUTE;
|
||||
end
|
||||
EXECUTE: begin
|
||||
case (1'b1)
|
||||
isALUreg: $display(
|
||||
"ALUreg rd=%d rs1=%d rs2=%d funct3=%b",
|
||||
rdId, rs1Id, rs2Id, funct3
|
||||
);
|
||||
isALUimm: $display(
|
||||
"ALUimm rd=%d rs1=%d imm=%0d funct3=%b",
|
||||
rdId, rs1Id, Iimm, funct3
|
||||
);
|
||||
isBranch: $display("BRANCH");
|
||||
isJAL: $display("JAL");
|
||||
isJALR: $display("JALR");
|
||||
isAUIPC: $display("AUIPC");
|
||||
isLUI: $display("LUI");
|
||||
isLoad: $display("LOAD");
|
||||
isStore: $display("STORE");
|
||||
isSYSTEM: $display("SYSTEM");
|
||||
endcase
|
||||
if(isSYSTEM) begin
|
||||
$finish();
|
||||
end
|
||||
PC <= PC + 1;
|
||||
state <= FETCH_INSTR;
|
||||
end
|
||||
endcase
|
||||
|
||||
if(writeBackEn && rdId != 0) begin
|
||||
RegisterBank[rdId] <= writeBackData;
|
||||
$display("x%0d <= %b",rdId,writeBackData);
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
endmodule
|
||||
|
||||
module bench();
|
||||
reg clock;
|
||||
wire [4:0] leds;
|
||||
|
||||
RiscV uut(
|
||||
.clock(clock),
|
||||
.leds(leds)
|
||||
);
|
||||
|
||||
initial begin
|
||||
clock = 0;
|
||||
forever begin
|
||||
#1 clock = ~clock;
|
||||
// $display("LEDS=%b",leds); // we will use the LEDs later
|
||||
end
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
||||
200
FemtoRV/TUTORIALS/FROM_BLINKER_TO_RISCV/step8.v
Normal file
200
FemtoRV/TUTORIALS/FROM_BLINKER_TO_RISCV/step8.v
Normal file
@@ -0,0 +1,200 @@
|
||||
/**
|
||||
* Step 8: Creating a RISC-V processor
|
||||
* Jumps
|
||||
* Usage:
|
||||
* iverilog step8.v
|
||||
* vvp a.out
|
||||
* to exit: <ctrl><c> then finish
|
||||
*/
|
||||
|
||||
`default_nettype none
|
||||
|
||||
module RiscV (
|
||||
input clock,
|
||||
output [4:0] leds
|
||||
);
|
||||
assign leds = 0; // we will use the LEDs later
|
||||
|
||||
reg [31:0] ROM [0:255];
|
||||
reg [31:0] PC; // program counter
|
||||
reg [31:0] instr; // current instruction
|
||||
|
||||
|
||||
`include "riscv_assembly.v"
|
||||
|
||||
// Initial value of program counter and instruction
|
||||
// register.
|
||||
initial begin
|
||||
PC = 0;
|
||||
instr = NOP_CODEOP;
|
||||
end
|
||||
|
||||
// ROM initialization, using our poor's men assembly
|
||||
// in "risc_assembly.v".
|
||||
initial begin
|
||||
ADD(x1,x0,x0);
|
||||
Label(L0_); ADDI(x1,x1,1);
|
||||
JAL(x0, LabelRef(L0_));
|
||||
EBREAK();
|
||||
end
|
||||
|
||||
|
||||
// See the table P. 105 in RISC-V manual
|
||||
// The 10 RISC-V instructions
|
||||
// Funny: what we do here is in fact just the reverse
|
||||
// of what's done in riscv_assembly.v !
|
||||
|
||||
wire isALUreg = (instr[6:0] == 7'b0110011); // rd <- rs1 OP rs2
|
||||
wire isALUimm = (instr[6:0] == 7'b0010011); // rd <- rs1 OP Iimm
|
||||
wire isBranch = (instr[6:0] == 7'b1100011); // if(rs1 OP rs2) PC<-PC+Bimm
|
||||
wire isJALR = (instr[6:0] == 7'b1100111); // rd <- PC+4; PC<-rs1+Iimm
|
||||
wire isJAL = (instr[6:0] == 7'b1101111); // rd <- PC+4; PC<-PC+Jimm
|
||||
wire isAUIPC = (instr[6:0] == 7'b0010111); // rd <- PC + Uimm
|
||||
wire isLUI = (instr[6:0] == 7'b0110111); // rd <- Uimm
|
||||
wire isLoad = (instr[6:0] == 7'b0000011); // rd <- mem[rs1+Iimm]
|
||||
wire isStore = (instr[6:0] == 7'b0100011); // mem[rs1+Simm] <- rs2
|
||||
wire isSYSTEM = (instr[6:0] == 7'b1110011); // special
|
||||
|
||||
// The 5 immediate formats
|
||||
wire [31:0] Uimm={ instr[31], instr[30:12], {12{1'b0}}};
|
||||
wire [31:0] Iimm={{21{instr[31]}}, instr[30:20]};
|
||||
wire [31:0] Simm={{21{instr[31]}}, instr[30:25],instr[11:7]};
|
||||
wire [31:0] Bimm={{20{instr[31]}}, instr[7],instr[30:25],instr[11:8],1'b0};
|
||||
wire [31:0] Jimm={{12{instr[31]}}, instr[19:12],instr[20],instr[30:21],1'b0};
|
||||
|
||||
// Source and destination registers
|
||||
wire [4:0] rs1Id = instr[19:15];
|
||||
wire [4:0] rs2Id = instr[24:20];
|
||||
wire [4:0] rdId = instr[11:7];
|
||||
|
||||
// function codes
|
||||
wire [2:0] funct3 = instr[14:12];
|
||||
wire [6:0] funct7 = instr[31:25];
|
||||
|
||||
// The registers bank
|
||||
reg [31:0] RegisterBank [0:31];
|
||||
reg [31:0] rs1; // value of source
|
||||
reg [31:0] rs2; // registers.
|
||||
wire [31:0] writeBackData; // data to be written to rd
|
||||
wire writeBackEn; // asserted if data should be written to rd
|
||||
|
||||
integer i;
|
||||
initial begin
|
||||
for(i=0; i<32; ++i) begin
|
||||
RegisterBank[i] = 0;
|
||||
end
|
||||
end
|
||||
|
||||
// The ALU
|
||||
wire [31:0] aluIn1 = rs1;
|
||||
wire [31:0] aluIn2 = isALUreg ? rs2 : Iimm;
|
||||
reg [31:0] aluOut;
|
||||
wire [4:0] shamt = isALUreg ? rs2[4:0] : instr[24:20]; // shift amount
|
||||
|
||||
always @(*) begin
|
||||
case(funct3)
|
||||
3'b000: aluOut = (funct7[5] & instr[5]) ? (aluIn1 - aluIn2) : (aluIn1 + aluIn2);
|
||||
3'b001: aluOut = aluIn1 << shamt;
|
||||
3'b010: aluOut = ($signed(aluIn1) < $signed(aluIn2));
|
||||
3'b011: aluOut = (aluIn1 < aluIn2);
|
||||
3'b100: aluOut = (aluIn1 ^ aluIn2);
|
||||
3'b101: aluOut = funct7[5]? ($signed(aluIn1) >>> shamt) : ($signed(aluIn1) >> shamt);
|
||||
3'b110: aluOut = (aluIn1 | aluIn2);
|
||||
3'b111: aluOut = (aluIn1 & aluIn2);
|
||||
endcase
|
||||
end
|
||||
|
||||
// ADD/SUB/ADDI:
|
||||
// funct7[5] is 1 for SUB and 0 for ADD. We need also to test instr[5]
|
||||
// to make the difference with ADDI
|
||||
//
|
||||
// SRLI/SRAI/SRL/SRA:
|
||||
// funct7[5] is 1 for arithmetic shift (SRA/SRAI) and 0 for logical shift (SRL/SRLI)
|
||||
|
||||
// The state machine
|
||||
localparam FETCH_INSTR = 0;
|
||||
localparam FETCH_REGS = 1;
|
||||
localparam EXECUTE = 2;
|
||||
reg [1:0] state;
|
||||
|
||||
// register write back
|
||||
assign writeBackData = (isJAL || isJALR) ? (PC + 4) : aluOut;
|
||||
assign writeBackEn = (state == EXECUTE && (isALUreg || isALUimm || isJAL || isJALR));
|
||||
|
||||
// next PC
|
||||
wire [31:0] nextPC =
|
||||
isJAL ? PC+Jimm :
|
||||
isJALR ? rs1+Iimm :
|
||||
PC+4;
|
||||
|
||||
|
||||
initial begin
|
||||
state = FETCH_INSTR;
|
||||
end
|
||||
|
||||
always @(posedge clock) begin
|
||||
case(state)
|
||||
FETCH_INSTR: begin
|
||||
instr <= ROM[PC[31:2]];
|
||||
state <= FETCH_REGS;
|
||||
end
|
||||
FETCH_REGS: begin
|
||||
rs1 <= RegisterBank[rs1Id];
|
||||
rs2 <= RegisterBank[rs2Id];
|
||||
state <= EXECUTE;
|
||||
end
|
||||
EXECUTE: begin
|
||||
case (1'b1)
|
||||
isALUreg: $display(
|
||||
"ALUreg rd=%d rs1=%d rs2=%d funct3=%b",
|
||||
rdId, rs1Id, rs2Id, funct3
|
||||
);
|
||||
isALUimm: $display(
|
||||
"ALUimm rd=%d rs1=%d imm=%0d funct3=%b",
|
||||
rdId, rs1Id, Iimm, funct3
|
||||
);
|
||||
isBranch: $display("BRANCH");
|
||||
isJAL: $display("JAL");
|
||||
isJALR: $display("JALR");
|
||||
isAUIPC: $display("AUIPC");
|
||||
isLUI: $display("LUI");
|
||||
isLoad: $display("LOAD");
|
||||
isStore: $display("STORE");
|
||||
isSYSTEM: $display("SYSTEM");
|
||||
endcase
|
||||
if(isSYSTEM) begin
|
||||
$finish();
|
||||
end
|
||||
PC <= nextPC;
|
||||
state <= FETCH_INSTR;
|
||||
end
|
||||
endcase
|
||||
|
||||
if(writeBackEn && rdId != 0) begin
|
||||
RegisterBank[rdId] <= writeBackData;
|
||||
$display("x%0d <= %b",rdId,writeBackData);
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
endmodule
|
||||
|
||||
module bench();
|
||||
reg clock;
|
||||
wire [4:0] leds;
|
||||
|
||||
RiscV uut(
|
||||
.clock(clock),
|
||||
.leds(leds)
|
||||
);
|
||||
|
||||
initial begin
|
||||
clock = 0;
|
||||
forever begin
|
||||
#1 clock = ~clock;
|
||||
// $display("LEDS=%b",leds); // we will use the LEDs later
|
||||
end
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
||||
217
FemtoRV/TUTORIALS/FROM_BLINKER_TO_RISCV/step9.v
Normal file
217
FemtoRV/TUTORIALS/FROM_BLINKER_TO_RISCV/step9.v
Normal file
@@ -0,0 +1,217 @@
|
||||
/**
|
||||
* Step 9: Creating a RISC-V processor
|
||||
* Branches
|
||||
* Usage:
|
||||
* iverilog step9.v
|
||||
* vvp a.out
|
||||
* to exit: <ctrl><c> then finish
|
||||
*/
|
||||
|
||||
`default_nettype none
|
||||
|
||||
module RiscV (
|
||||
input clock,
|
||||
output [4:0] leds
|
||||
);
|
||||
assign leds = 0; // we will use the LEDs later
|
||||
|
||||
reg [31:0] ROM [0:255];
|
||||
reg [31:0] PC; // program counter
|
||||
reg [31:0] instr; // current instruction
|
||||
|
||||
|
||||
`include "riscv_assembly.v"
|
||||
|
||||
// Initial value of program counter and instruction
|
||||
// register.
|
||||
initial begin
|
||||
PC = 0;
|
||||
instr = NOP_CODEOP;
|
||||
end
|
||||
|
||||
// ROM initialization, using our poor's men assembly
|
||||
// in "risc_assembly.v".
|
||||
initial begin
|
||||
ADD(x1,x0,x0);
|
||||
ADDI(x2,x0,32);
|
||||
Label(L0_); ADDI(x1,x1,1);
|
||||
BNE(x1, x2, LabelRef(L0_));
|
||||
EBREAK();
|
||||
end
|
||||
|
||||
|
||||
// See the table P. 105 in RISC-V manual
|
||||
// The 10 RISC-V instructions
|
||||
// Funny: what we do here is in fact just the reverse
|
||||
// of what's done in riscv_assembly.v !
|
||||
|
||||
wire isALUreg = (instr[6:0] == 7'b0110011); // rd <- rs1 OP rs2
|
||||
wire isALUimm = (instr[6:0] == 7'b0010011); // rd <- rs1 OP Iimm
|
||||
wire isBranch = (instr[6:0] == 7'b1100011); // if(rs1 OP rs2) PC<-PC+Bimm
|
||||
wire isJALR = (instr[6:0] == 7'b1100111); // rd <- PC+4; PC<-rs1+Iimm
|
||||
wire isJAL = (instr[6:0] == 7'b1101111); // rd <- PC+4; PC<-PC+Jimm
|
||||
wire isAUIPC = (instr[6:0] == 7'b0010111); // rd <- PC + Uimm
|
||||
wire isLUI = (instr[6:0] == 7'b0110111); // rd <- Uimm
|
||||
wire isLoad = (instr[6:0] == 7'b0000011); // rd <- mem[rs1+Iimm]
|
||||
wire isStore = (instr[6:0] == 7'b0100011); // mem[rs1+Simm] <- rs2
|
||||
wire isSYSTEM = (instr[6:0] == 7'b1110011); // special
|
||||
|
||||
// The 5 immediate formats
|
||||
wire [31:0] Uimm={ instr[31], instr[30:12], {12{1'b0}}};
|
||||
wire [31:0] Iimm={{21{instr[31]}}, instr[30:20]};
|
||||
wire [31:0] Simm={{21{instr[31]}}, instr[30:25],instr[11:7]};
|
||||
wire [31:0] Bimm={{20{instr[31]}}, instr[7],instr[30:25],instr[11:8],1'b0};
|
||||
wire [31:0] Jimm={{12{instr[31]}}, instr[19:12],instr[20],instr[30:21],1'b0};
|
||||
|
||||
// Source and destination registers
|
||||
wire [4:0] rs1Id = instr[19:15];
|
||||
wire [4:0] rs2Id = instr[24:20];
|
||||
wire [4:0] rdId = instr[11:7];
|
||||
|
||||
// function codes
|
||||
wire [2:0] funct3 = instr[14:12];
|
||||
wire [6:0] funct7 = instr[31:25];
|
||||
|
||||
// The registers bank
|
||||
reg [31:0] RegisterBank [0:31];
|
||||
reg [31:0] rs1; // value of source
|
||||
reg [31:0] rs2; // registers.
|
||||
wire [31:0] writeBackData; // data to be written to rd
|
||||
wire writeBackEn; // asserted if data should be written to rd
|
||||
|
||||
integer i;
|
||||
initial begin
|
||||
for(i=0; i<32; ++i) begin
|
||||
RegisterBank[i] = 0;
|
||||
end
|
||||
end
|
||||
|
||||
// The ALU
|
||||
wire [31:0] aluIn1 = rs1;
|
||||
wire [31:0] aluIn2 = isALUreg ? rs2 : Iimm;
|
||||
reg [31:0] aluOut;
|
||||
wire [4:0] shamt = isALUreg ? rs2[4:0] : instr[24:20]; // shift amount
|
||||
|
||||
always @(*) begin
|
||||
case(funct3)
|
||||
3'b000: aluOut = (funct7[5] & instr[5]) ? (aluIn1 - aluIn2) : (aluIn1 + aluIn2);
|
||||
3'b001: aluOut = aluIn1 << shamt;
|
||||
3'b010: aluOut = ($signed(aluIn1) < $signed(aluIn2));
|
||||
3'b011: aluOut = (aluIn1 < aluIn2);
|
||||
3'b100: aluOut = (aluIn1 ^ aluIn2);
|
||||
3'b101: aluOut = funct7[5]? ($signed(aluIn1) >>> shamt) : ($signed(aluIn1) >> shamt);
|
||||
3'b110: aluOut = (aluIn1 | aluIn2);
|
||||
3'b111: aluOut = (aluIn1 & aluIn2);
|
||||
endcase
|
||||
end
|
||||
|
||||
// ADD/SUB/ADDI:
|
||||
// funct7[5] is 1 for SUB and 0 for ADD. We need also to test instr[5]
|
||||
// to make the difference with ADDI
|
||||
//
|
||||
// SRLI/SRAI/SRL/SRA:
|
||||
// funct7[5] is 1 for arithmetic shift (SRA/SRAI) and 0 for logical shift (SRL/SRLI)
|
||||
|
||||
reg takeBranch;
|
||||
always @(*) begin
|
||||
case(funct3)
|
||||
3'b000: takeBranch = (rs1 == rs2);
|
||||
3'b001: takeBranch = (rs1 != rs2);
|
||||
3'b100: takeBranch = ($signed(rs1) < $signed(rs2));
|
||||
3'b101: takeBranch = ($signed(rs1) >= $signed(rs2));
|
||||
3'b110: takeBranch = (rs1 < rs2);
|
||||
3'b110: takeBranch = (rs1 >= rs2);
|
||||
default: takeBranch = 1'b0;
|
||||
endcase
|
||||
end
|
||||
|
||||
// The state machine
|
||||
localparam FETCH_INSTR = 0;
|
||||
localparam FETCH_REGS = 1;
|
||||
localparam EXECUTE = 2;
|
||||
reg [1:0] state;
|
||||
|
||||
// register write back
|
||||
assign writeBackData = (isJAL || isJALR) ? (PC + 4) : aluOut;
|
||||
assign writeBackEn = (state == EXECUTE && (isALUreg || isALUimm || isJAL || isJALR));
|
||||
|
||||
// next PC
|
||||
wire [31:0] nextPC =
|
||||
(isBranch && takeBranch) ? PC+Bimm :
|
||||
isJAL ? PC+Jimm :
|
||||
isJALR ? rs1+Iimm :
|
||||
PC+4;
|
||||
|
||||
initial begin
|
||||
state = FETCH_INSTR;
|
||||
end
|
||||
|
||||
always @(posedge clock) begin
|
||||
case(state)
|
||||
FETCH_INSTR: begin
|
||||
instr <= ROM[PC[31:2]];
|
||||
state <= FETCH_REGS;
|
||||
end
|
||||
FETCH_REGS: begin
|
||||
rs1 <= RegisterBank[rs1Id];
|
||||
rs2 <= RegisterBank[rs2Id];
|
||||
state <= EXECUTE;
|
||||
end
|
||||
EXECUTE: begin
|
||||
case (1'b1)
|
||||
isALUreg: $display(
|
||||
"ALUreg rd=%d rs1=%d rs2=%d funct3=%b",
|
||||
rdId, rs1Id, rs2Id, funct3
|
||||
);
|
||||
isALUimm: $display(
|
||||
"ALUimm rd=%d rs1=%d imm=%0d funct3=%b",
|
||||
rdId, rs1Id, Iimm, funct3
|
||||
);
|
||||
isBranch: $display(
|
||||
"BRANCH rs1=%d rs2=%d takeBranch=%b",
|
||||
rs1Id, rs2Id, takeBranch
|
||||
);
|
||||
isJAL: $display("JAL");
|
||||
isJALR: $display("JALR");
|
||||
isAUIPC: $display("AUIPC");
|
||||
isLUI: $display("LUI");
|
||||
isLoad: $display("LOAD");
|
||||
isStore: $display("STORE");
|
||||
isSYSTEM: $display("SYSTEM");
|
||||
endcase
|
||||
if(isSYSTEM) begin
|
||||
$finish();
|
||||
end
|
||||
PC <= nextPC;
|
||||
state <= FETCH_INSTR;
|
||||
end
|
||||
endcase
|
||||
|
||||
if(writeBackEn && rdId != 0) begin
|
||||
RegisterBank[rdId] <= writeBackData;
|
||||
$display("x%0d <= %b",rdId,writeBackData);
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
endmodule
|
||||
|
||||
module bench();
|
||||
reg clock;
|
||||
wire [4:0] leds;
|
||||
|
||||
RiscV uut(
|
||||
.clock(clock),
|
||||
.leds(leds)
|
||||
);
|
||||
|
||||
initial begin
|
||||
clock = 0;
|
||||
forever begin
|
||||
#1 clock = ~clock;
|
||||
// $display("LEDS=%b",leds); // we will use the LEDs later
|
||||
end
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
||||
@@ -62,7 +62,7 @@ missing component that I needed to create is:
|
||||
- a loader for the ELF format. I wrote a super
|
||||
simple one [here](https://github.com/BrunoLevy/learn-fpga/blob/master/LiteX/software/Libs/lite_elf.c).
|
||||
The technical details are described [here](https://github.com/BrunoLevy/learn-fpga/blob/master/FemtoRV/TUTORIALS/software.md).
|
||||
The result is a few hundred lines of with no dependencies that can directly load statically-linked ELFs.
|
||||
The result is a few hundred lines of C with no dependencies that can directly load statically-linked ELFs.
|
||||
|
||||
Then the rest works as follows:
|
||||
|
||||
|
||||
Reference in New Issue
Block a user