newStep.v
This commit is contained in:
188
RTL/Attic/mini_decoder.v
Normal file
188
RTL/Attic/mini_decoder.v
Normal file
@@ -0,0 +1,188 @@
|
||||
/********************* Instruction decoder *******************************/
|
||||
// A drop-in replacement of the instruction decoder, meant to further
|
||||
// reduce LUT count by not checking for errors.
|
||||
// Optimized by @mecrisp
|
||||
// in femtorv32.v, replace `include "decoder.v"
|
||||
// with `include "mini_decoder.v"
|
||||
// (does not seem to save many LUTs with my version of YOSYS, but it depends).
|
||||
// NOTE: the structure of the decoder has changed, *** NEEDS TO BE ADAPTED ***
|
||||
|
||||
module NrvDecoder(
|
||||
input wire [31:0] instr,
|
||||
output wire [4:0] writeBackRegId,
|
||||
output reg writeBackEn,
|
||||
output reg [3:0] writeBackSel, // 0001: ALU 0010: PC+4 0100: RAM 1000: counters
|
||||
// (could use 2 wires instead, but using 4 wires (1-hot encoding)
|
||||
// reduces both LUT count and critical path in the end !)
|
||||
output wire [4:0] inRegId1,
|
||||
output wire [4:0] inRegId2,
|
||||
output reg aluSel, // 0: force aluOp,aluQual to zero (ADD) 1: use aluOp,aluQual from instr field
|
||||
output reg aluInSel1, // 0: reg 1: pc
|
||||
output reg aluInSel2, // 0: reg 1: imm
|
||||
output [2:0] aluOp,
|
||||
output reg aluQual,
|
||||
output wire aluM, // Asserted if operation is an RV32M operation
|
||||
output reg isLoad,
|
||||
output reg isStore,
|
||||
output reg isJump,
|
||||
output reg isBranch,
|
||||
output reg needWaitALU,
|
||||
output reg [31:0] imm,
|
||||
output wire error
|
||||
);
|
||||
|
||||
assign error = 1'b0; // We do not check for errors in the MiniDecoder.
|
||||
assign aluM = 1'b0; // MiniDecoder only works for RV32I
|
||||
|
||||
|
||||
reg inRegId1Sel; // 0: force inRegId1 to zero 1: use inRegId1 instr field
|
||||
|
||||
assign writeBackRegId = instr[11:7];
|
||||
assign inRegId1 = instr[19:15] & {5{inRegId1Sel}}; // Internal sig InRegId1Sel used to force zero in reg1
|
||||
assign inRegId2 = instr[24:20]; // (because I'm making maximum reuse of the adder of the ALU)
|
||||
assign aluOp = instr[14:12];
|
||||
|
||||
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};
|
||||
wire [31:0] Uimm = {instr[31], instr[30:12], {12{1'b0}}};
|
||||
|
||||
// The rest of instruction decoding, for the following signals:
|
||||
// writeBackEn
|
||||
// writeBackSel 0001: ALU 0010: PC+4 0100: RAM 1000: counters
|
||||
// inRegId1Sel 0: zero 1: regId
|
||||
// aluInSel1 0: reg 1: PC
|
||||
// aluInSel2 0: reg 1: imm
|
||||
// aluQual +/- SRLI/SRAI
|
||||
// aluM 1 if instr is RV32M
|
||||
// aluSel 0: force aluOp,aluQual=00 1: use aluOp/aluQual
|
||||
// nextPCSel 001: PC+4 010: ALU 100: (pred ? ALU : PC+4)
|
||||
// imm (select one of Iimm,Simm,Bimm,Jimm,Uimm)
|
||||
|
||||
// We need to distingish shifts for two reasons:
|
||||
// - We need to wait for ALU when it is a shift
|
||||
// - For ALU ops with immediates, aluQual is 0, except
|
||||
// for shifts (then it is instr[30]).
|
||||
wire aluOpIsShift = (aluOp == 3'b001) || (aluOp == 3'b101);
|
||||
|
||||
always @(*) begin
|
||||
|
||||
inRegId1Sel = 1'b1; // reg 1 Id from instr
|
||||
isLoad = 1'b0;
|
||||
isStore = 1'b0;
|
||||
isJump = 1'b0;
|
||||
isBranch = 1'b0;
|
||||
aluQual = 1'b0;
|
||||
needWaitALU = 1'b0;
|
||||
|
||||
(* parallel_case, full_case *)
|
||||
casez(instr[6:2])
|
||||
5'b011?1: begin // LUI
|
||||
writeBackEn = 1'b1; // enable write back
|
||||
writeBackSel = 4'b0001; // write back source = ALU
|
||||
inRegId1Sel = 1'b0; // reg 1 Id = 0
|
||||
aluInSel1 = 1'b0; // ALU source 1 = reg
|
||||
aluInSel2 = 1'b1; // ALU source 2 = imm
|
||||
aluSel = 1'b0; // ALU op = ADD
|
||||
imm = Uimm; // imm format = U
|
||||
end
|
||||
|
||||
5'b001?1: begin // AUIPC
|
||||
writeBackEn = 1'b1; // enable write back
|
||||
writeBackSel = 4'b0001; // write back source = ALU
|
||||
inRegId1Sel = 1'bx; // reg 1 Id : don't care (we use PC)
|
||||
aluInSel1 = 1'b1; // ALU source 1 = PC
|
||||
aluInSel2 = 1'b1; // ALU source 2 = imm
|
||||
aluSel = 1'b0; // ALU op = ADD
|
||||
imm = Uimm; // imm format = U
|
||||
end
|
||||
|
||||
5'b11011: begin // JAL
|
||||
writeBackEn = 1'b1; // enable write back
|
||||
writeBackSel = 4'b0010; // write back source = PC+4
|
||||
inRegId1Sel = 1'bx; // reg 1 Id : don't care (we use PC)
|
||||
aluInSel1 = 1'b1; // ALU source 1 = PC
|
||||
aluInSel2 = 1'b1; // ALU source 2 = imm
|
||||
aluSel = 1'b0; // ALU op = ADD
|
||||
isJump = 1'b1; // PC <- ALU
|
||||
imm = Jimm; // imm format = J
|
||||
end
|
||||
|
||||
5'b11001: begin // JALR
|
||||
writeBackEn = 1'b1; // enable write back
|
||||
writeBackSel = 4'b0010; // write back source = PC+4
|
||||
aluInSel1 = 1'b0; // ALU source 1 = reg
|
||||
aluInSel2 = 1'b1; // ALU source 2 = imm
|
||||
aluSel = 1'b0; // ALU op = ADD
|
||||
isJump = 1'b1; // PC <- ALU
|
||||
imm = Iimm; // imm format = I
|
||||
end
|
||||
|
||||
5'b110?0: begin // Branch
|
||||
writeBackEn = 1'b0; // disable write back
|
||||
writeBackSel = 4'bxxxx; // write back source = don't care
|
||||
aluInSel1 = 1'b1; // ALU source 1 : PC
|
||||
aluInSel2 = 1'b1; // ALU source 2 : imm
|
||||
aluSel = 1'b0; // ALU op = ADD
|
||||
isBranch = 1'b1; // PC <- pred ? ALU : PC+4
|
||||
imm = Bimm; // imm format = B
|
||||
end
|
||||
|
||||
5'b001?0: begin // ALU operation: Register,Immediate
|
||||
writeBackEn = 1'b1; // enable write back
|
||||
writeBackSel = 4'b0001; // write back source = ALU
|
||||
aluInSel1 = 1'b0; // ALU source 1 : reg
|
||||
aluInSel2 = 1'b1; // ALU source 2 : imm
|
||||
// Qualifier for ALU op: SRLI/SRAI
|
||||
aluQual = aluOpIsShift ? instr[30] : 1'b0;
|
||||
needWaitALU = aluOpIsShift;
|
||||
aluSel = 1'b1; // ALU op : from instr
|
||||
imm = Iimm; // imm format = I
|
||||
end
|
||||
|
||||
5'b011?0: begin // ALU operation: Register,Register
|
||||
writeBackEn = 1'b1; // enable write back
|
||||
writeBackSel = 4'b0001; // write back source = ALU
|
||||
aluInSel1 = 1'b0; // ALU source 1 : reg
|
||||
aluInSel2 = 1'b0; // ALU source 2 : reg
|
||||
aluQual = instr[30]; // Qualifier for ALU op: +/- SRL/SRA
|
||||
aluSel = 1'b1; // ALU op : from instr
|
||||
needWaitALU = aluOpIsShift;
|
||||
imm = 32'bxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx; // don't care
|
||||
end
|
||||
|
||||
5'b000?0: begin // Load
|
||||
writeBackEn = 1'b1; // enable write back
|
||||
writeBackSel = 4'b0100; // write back source = RAM
|
||||
aluInSel1 = 1'b0; // ALU source 1 = reg
|
||||
aluInSel2 = 1'b1; // ALU source 2 = imm
|
||||
aluSel = 1'b0; // ALU op = ADD
|
||||
imm = Iimm; // imm format = I
|
||||
isLoad = 1'b1;
|
||||
end
|
||||
|
||||
5'b010?0: begin // Store
|
||||
writeBackEn = 1'b0; // disable write back
|
||||
writeBackSel = 4'bxxxx; // write back sel = don't care
|
||||
aluInSel1 = 1'b0; // ALU source 1 = reg
|
||||
aluInSel2 = 1'b1; // ALU source 2 = imm
|
||||
aluSel = 1'b0; // ALU op = ADD
|
||||
imm = Simm; // imm format = S
|
||||
isStore = 1'b1;
|
||||
end
|
||||
|
||||
default: begin
|
||||
writeBackEn = 1'b0;
|
||||
writeBackSel = 4'bxxxx;
|
||||
inRegId1Sel = 1'bx;
|
||||
aluInSel1 = 1'bx;
|
||||
aluInSel2 = 1'bx;
|
||||
aluSel = 1'bx;
|
||||
imm = 32'bxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx;
|
||||
end
|
||||
endcase
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
||||
456
RTL/Attic/mini_femtorv32.v
Normal file
456
RTL/Attic/mini_femtorv32.v
Normal file
@@ -0,0 +1,456 @@
|
||||
// femtorv32, a minimalistic RISC-V RV32I core
|
||||
// (minus SYSTEM and FENCE that are not implemented)
|
||||
// Bruno Levy, May-June 2020
|
||||
//
|
||||
// drop-in replacement of femtorv32,
|
||||
// does 3 CPIs (cycles per instructions) in linear execution flow
|
||||
// (two be compared with 2 CPIs with femtorv32.v),
|
||||
// saves 20-50 LUTs
|
||||
// in femtosoc.v, replace `include "femtorv32.v"
|
||||
// with `include "mini_femtorv32.v"
|
||||
//
|
||||
// NOTE: the structure of the decoder has changed, *** NEEDS TO BE ADAPTED ***
|
||||
|
||||
|
||||
/*******************************************************************/
|
||||
|
||||
`include "utils.v" // Utilities, macros for debugging
|
||||
`include "register_file.v" // The 31 general-purpose registers
|
||||
`include "small_alu.v" // Used on IceStick, RV32I
|
||||
`include "large_alu.v" // For larger FPGAs, RV32IM
|
||||
`include "branch_predicates.v" // Tests for branch instructions
|
||||
`include "decoder.v" // The instruction decoder
|
||||
`include "aligned_memory_access.v" // Read/write bytes, hwords and words from memory
|
||||
`include "CSR_file.v" // (Optional) Control and Status registers
|
||||
|
||||
/********************* Nrv processor *******************************/
|
||||
|
||||
module FemtoRV32 #(
|
||||
parameter [0:0] RV32M = 0, // Set to 1 to support mul/div/rem instructions
|
||||
parameter ADDR_WIDTH = 16 // width of the address bus
|
||||
) (
|
||||
input clk,
|
||||
|
||||
// Memory interface: using the same protocol as Claire Wolf's picoR32
|
||||
// (WIP: add mem_valid / mem_ready protocol)
|
||||
output [31:0] mem_addr, // address bus, only ADDR_WIDTH bits are used
|
||||
output wire [31:0] mem_wdata, // data to be written
|
||||
output wire [3:0] mem_wmask, // write mask for individual bytes (1 means write byte)
|
||||
input [31:0] mem_rdata, // input lines for both data and instr
|
||||
output wire mem_rstrb, // active to initiate memory read
|
||||
input wire mem_rbusy, // asserted if memory is busy reading value
|
||||
input wire mem_wbusy, // asserted if memory is busy writing value
|
||||
|
||||
input wire reset, // set to 0 to reset the processor
|
||||
output wire error // 1 if current instruction could not be decoded
|
||||
);
|
||||
|
||||
|
||||
// The internal register that stores the current address,
|
||||
// directly wired to the address bus.
|
||||
reg [ADDR_WIDTH-1:0] addressReg;
|
||||
|
||||
// The program counter (not storing the two LSBs, always aligned)
|
||||
reg [ADDR_WIDTH-3:0] PC;
|
||||
|
||||
assign mem_addr = addressReg;
|
||||
|
||||
reg [31:0] instr; // Latched instruction.
|
||||
reg [31:0] nextInstr; // Prefetched instruction.
|
||||
|
||||
|
||||
// Next program counter in normal operation: advance one word
|
||||
// I do not use the ALU, I create an additional adder for that.
|
||||
// (not that the two LSBs are not stored, always aligned).
|
||||
wire [ADDR_WIDTH-3:0] PCplus4 = PC + 1;
|
||||
|
||||
/**************************************************************************************************/
|
||||
// Instruction decoding.
|
||||
|
||||
// Internal signals, all generated by the decoder from the current instruction.
|
||||
wire [4:0] writeBackRegId; // The register to be written back
|
||||
wire writeBackEn; // Needs to be asserted for writing back
|
||||
wire [3:0] writeBackSel; // 0001: ALU 0010: PC+4 0100: RAM 1000: CSR
|
||||
wire [4:0] regId1; // Register output 1
|
||||
wire [4:0] regId2; // Register output 2
|
||||
wire aluInSel1; // 0: register 1: pc
|
||||
wire aluInSel2; // 0: register 1: imm
|
||||
wire aluSel; // 0: force aluOp,aluQual to zero (ADD) 1: use aluOp,aluQual from instr field
|
||||
wire [2:0] aluOp; // one of the 8 operations done by the ALU
|
||||
wire aluQual; // 'qualifier' used by some operations (+/-, logical/arith shifts)
|
||||
wire aluM; // asserted if instr is RV32M.
|
||||
wire [31:0] imm; // immediate value decoded from the instruction
|
||||
wire needWaitALU; // asserted if instruction uses at least one additional phase in ALU
|
||||
wire isLoad; // guess what
|
||||
wire isStore; // guess what
|
||||
wire isJump; // guess what
|
||||
wire isBranch; // guess what
|
||||
wire decoderError; // true if instr does not correspond to any known instr
|
||||
|
||||
// The instruction decoder, that reads the current instruction
|
||||
// and generates all the signals from it. It is in fact just a
|
||||
// big combinatorial function.
|
||||
NrvDecoder decoder(
|
||||
.instr(instr),
|
||||
.writeBackRegId(writeBackRegId),
|
||||
.writeBackEn(writeBackEn),
|
||||
.writeBackSel(writeBackSel),
|
||||
.inRegId1(regId1),
|
||||
.inRegId2(regId2),
|
||||
.aluInSel1(aluInSel1),
|
||||
.aluInSel2(aluInSel2),
|
||||
.aluSel(aluSel),
|
||||
.aluOp(aluOp),
|
||||
.aluQual(aluQual),
|
||||
.aluM(aluM),
|
||||
.needWaitALU(needWaitALU),
|
||||
.isLoad(isLoad),
|
||||
.isStore(isStore),
|
||||
.isJump(isJump),
|
||||
.isBranch(isBranch),
|
||||
.imm(imm),
|
||||
.error(decoderError)
|
||||
);
|
||||
|
||||
/**************************************************************************************************/
|
||||
// Maybe not necessary, but I'd rather latch this one,
|
||||
// if this one glitches, then it will break everything...
|
||||
reg error_latched;
|
||||
assign error = error_latched;
|
||||
|
||||
/**************************************************************************************************/
|
||||
// The register file. At each cycle, it can read two
|
||||
// registers (available at next cycle) and write one.
|
||||
wire writeBack;
|
||||
|
||||
reg [31:0] writeBackData;
|
||||
wire [31:0] regOut1;
|
||||
wire [31:0] regOut2;
|
||||
NrvRegisterFile regs(
|
||||
.clk(clk),
|
||||
.in(writeBackData),
|
||||
.inEn(writeBack),
|
||||
.inRegId(writeBackRegId),
|
||||
.outRegId1(regId1),
|
||||
.outRegId2(regId2),
|
||||
.out1(regOut1),
|
||||
.out2(regOut2)
|
||||
);
|
||||
|
||||
/**************************************************************************************************/
|
||||
// The ALU, partly combinatorial, partly state (for shifts).
|
||||
wire [31:0] aluOut;
|
||||
wire aluBusy;
|
||||
wire alu_wenable;
|
||||
wire [31:0] aluIn1 = aluInSel1 ? {PC, 2'b00} : regOut1;
|
||||
wire [31:0] aluIn2 = aluInSel2 ? imm : regOut2;
|
||||
|
||||
// Select the ALU based on RV32M (use large ALU) or plain RV32I (use small ALU)
|
||||
generate
|
||||
if(RV32M) begin
|
||||
NrvLargeALU alu(
|
||||
.clk(clk),
|
||||
.in1(aluIn1),
|
||||
.in2(aluIn2),
|
||||
.op(aluOp & {3{aluSel}}),
|
||||
.opqual(aluQual & aluSel),
|
||||
.opM(aluM),
|
||||
.out(aluOut),
|
||||
.wr(alu_wenable),
|
||||
.busy(aluBusy)
|
||||
);
|
||||
end else begin
|
||||
NrvSmallALU #(
|
||||
`ifdef NRV_TWOSTAGE_SHIFTER
|
||||
.TWOSTAGE_SHIFTER(1)
|
||||
`else
|
||||
.TWOSTAGE_SHIFTER(0)
|
||||
`endif
|
||||
) alu(
|
||||
.clk(clk),
|
||||
.in1(aluIn1),
|
||||
.in2(aluIn2),
|
||||
.op(aluOp & {3{aluSel}}),
|
||||
.opqual(aluQual & aluSel),
|
||||
.out(aluOut),
|
||||
.wr(alu_wenable),
|
||||
.busy(aluBusy)
|
||||
);
|
||||
end
|
||||
endgenerate
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
// Memory only does 32-bit aligned accesses. Internally we have two small
|
||||
// circuits (one for LOAD and one for STORE) that shift and adapt data
|
||||
// according to data type (byte, halfword, word) and memory alignment (addr[1:0]).
|
||||
// In addition, it does sign-expansion (when loading a signed byte to a word for
|
||||
// instance).
|
||||
|
||||
// LOAD: a small combinatorial circuit that realigns
|
||||
// and sign-expands mem_rdata based
|
||||
// on width (aluOp[1:0]), signed/unsigned flag (aluOp[2])
|
||||
// and the two LSBs of the address.
|
||||
wire [31:0] LOAD_mem_rdata_aligned;
|
||||
NrvLoadFromMemory load_from_mem(
|
||||
.mem_rdata(mem_rdata), // Raw data read from mem
|
||||
.addr_LSBs(mem_addr[1:0]), // The two LSBs of the address
|
||||
.width(aluOp[1:0]), // Data width: 00:byte 01:hword 10:word
|
||||
.is_unsigned(aluOp[2]), // signed/unsigned flag
|
||||
.data(LOAD_mem_rdata_aligned) // Data ready to be sent to register
|
||||
);
|
||||
|
||||
// STORE: a small combinatorial circuit that realigns
|
||||
// data to be written based on width and the two LSBs
|
||||
// of the address.
|
||||
// When a STORE instruction is executed, the data to be stored to
|
||||
// mem is available from the second register (regOut2) and the
|
||||
// address where to store it is the output of the ALU (aluOut).
|
||||
wire mem_wenable;
|
||||
NrvStoreToMemory store_to_mem(
|
||||
.data(regOut2), // Data to be sent, out of register
|
||||
.addr_LSBs(aluOut[1:0]), // The two LSBs of the address
|
||||
.width(aluOp[1:0]), // Data width: 00:byte 01:hword 10:word
|
||||
.mem_wdata(mem_wdata), // Shifted data to be sent to memory
|
||||
.mem_wmask(mem_wmask), // Write mask for the 4 bytes
|
||||
.wr_enable(mem_wenable) // Write enable ('anded' with write mask)
|
||||
);
|
||||
|
||||
/*************************************************************************/
|
||||
// Control and status registers
|
||||
|
||||
`ifdef NRV_CSR
|
||||
wire [31:0] CSR_rdata;
|
||||
wire instr_retired;
|
||||
NrvControlStatusRegisterFile CSR(
|
||||
.clk(clk), // for counting cycles
|
||||
.instr_cnt(instr_retired), // for counting retired instructions
|
||||
.reset(reset), // reset all CSRs to default value
|
||||
.CSRid(instr[31:20]), // CSR Id, extracted from instr
|
||||
.rdata(CSR_rdata) // Read CSR value
|
||||
// TODO: test for errors (.error)
|
||||
);
|
||||
`endif
|
||||
// Note: writing to CSRs not implemented yet
|
||||
|
||||
|
||||
/*************************************************************************/
|
||||
// The value written back to the register file.
|
||||
|
||||
always @(*) begin
|
||||
(* parallel_case, full_case *)
|
||||
case(1'b1)
|
||||
writeBackSel[0]: writeBackData = aluOut;
|
||||
writeBackSel[1]: writeBackData = {PCplus4, 2'b00};
|
||||
writeBackSel[2]: writeBackData = LOAD_mem_rdata_aligned;
|
||||
`ifdef NRV_CSR
|
||||
writeBackSel[3]: writeBackData = CSR_rdata;
|
||||
`endif
|
||||
endcase
|
||||
end
|
||||
|
||||
/*************************************************************************/
|
||||
// The predicate for conditional branches.
|
||||
|
||||
wire predOut;
|
||||
NrvPredicate pred(
|
||||
.in1(regOut1),
|
||||
.in2(regOut2),
|
||||
.op(aluOp),
|
||||
.out(predOut)
|
||||
);
|
||||
|
||||
/*************************************************************************/
|
||||
// And, last but not least, the state machine.
|
||||
/*************************************************************************/
|
||||
|
||||
// The states, using 1-hot encoding (reduces
|
||||
// both LUT count and critical path).
|
||||
|
||||
localparam INITIAL = 8'b00000000;
|
||||
localparam WAIT_INSTR = 8'b00000001;
|
||||
localparam FETCH_INSTR = 8'b00000010;
|
||||
localparam USE_PREFETCHED_INSTR = 8'b00000100;
|
||||
localparam FETCH_REGS = 8'b00001000;
|
||||
localparam EXECUTE = 8'b00010000;
|
||||
localparam WAIT_ALU_OR_DATA = 8'b00100000;
|
||||
localparam LOAD = 8'b01000000;
|
||||
localparam ERROR = 8'b10000000;
|
||||
|
||||
localparam WAIT_INSTR_bit = 0;
|
||||
localparam FETCH_INSTR_bit = 1;
|
||||
localparam USE_PREFETCHED_INSTR_bit = 2;
|
||||
localparam FETCH_REGS_bit = 3;
|
||||
localparam EXECUTE_bit = 4;
|
||||
localparam WAIT_ALU_OR_DATA_bit = 5;
|
||||
localparam LOAD_bit = 6;
|
||||
localparam ERROR_bit = 7;
|
||||
|
||||
reg [7:0] state = INITIAL;
|
||||
|
||||
// the internal signals that are determined combinatorially from
|
||||
// state and other signals.
|
||||
|
||||
// The internal signal that enables register write-back
|
||||
assign writeBack = (state[EXECUTE_bit] && writeBackEn) || state[WAIT_ALU_OR_DATA_bit];
|
||||
|
||||
// The memory-read signal. It is only needed for IO, hence it is only enabled
|
||||
// right before the LOAD state. To allow execution from IO-mapped devices, it
|
||||
// will be necessary to also enable it before instruction fetch.
|
||||
assign mem_rstrb = (state[EXECUTE_bit] && isLoad);
|
||||
|
||||
// NOTE: memory write are done during the USE_PREFETCHED_INSTR state,
|
||||
// Can't be done during EXECUTE (would be better), because mem_addr
|
||||
// (needed) is updated at the end of EXECUTE.
|
||||
// See also how load_from_mem and store_to_mem are wired.
|
||||
assign mem_wenable = (state[USE_PREFETCHED_INSTR_bit] && isStore);
|
||||
|
||||
// alu_wenable starts computation in the ALU (for functions that
|
||||
// require several cycles).
|
||||
assign alu_wenable = (state[EXECUTE_bit]);
|
||||
|
||||
// instr_retired is asserted during one cycle for each
|
||||
// retired instructions. It is used to update the instruction
|
||||
// counter 'instret' in the control and status registers
|
||||
`ifdef NRV_CSR
|
||||
assign instr_retired = state[FETCH_REGS_bit];
|
||||
`endif
|
||||
|
||||
// And now the state machine
|
||||
|
||||
`define show_state(state) `verbose($display(" %s",state))
|
||||
|
||||
always @(posedge clk) begin
|
||||
if(!reset) begin
|
||||
state <= INITIAL;
|
||||
addressReg <= 0;
|
||||
PC <= 0;
|
||||
end else
|
||||
case(1'b1)
|
||||
(state == 0): begin
|
||||
`show_state("initial");
|
||||
state <= WAIT_INSTR;
|
||||
end
|
||||
state[WAIT_INSTR_bit]: begin
|
||||
`show_state("wait_instr");
|
||||
// this state to give enough time to fetch the
|
||||
// instruction. Used for jumps and taken branches (and
|
||||
// when fetching the first instruction).
|
||||
state <= FETCH_INSTR;
|
||||
end
|
||||
state[FETCH_INSTR_bit]: begin
|
||||
`show_state("fetch_instr");
|
||||
instr <= mem_rdata;
|
||||
// update instr address so that next instr is fetched during
|
||||
// decode (and ready if there was no jump or branch)
|
||||
addressReg <= {PCplus4, 2'b00};
|
||||
state <= FETCH_REGS;
|
||||
end
|
||||
state[USE_PREFETCHED_INSTR_bit]: begin
|
||||
`show_state("use_prefetched_instr");
|
||||
// for linear execution flow, the prefetched isntr (nextInstr)
|
||||
// can be used.
|
||||
instr <= nextInstr;
|
||||
// update instr address so that next instr is fetched during
|
||||
// decode (and ready if there was no jump or branch)
|
||||
addressReg <= {PCplus4, 2'b00};
|
||||
// In addition, STORE instructions write to memory here.
|
||||
// (see NrvStoreToMemory store_to_mem at beginning of file).
|
||||
state <= FETCH_REGS;
|
||||
end
|
||||
state[FETCH_REGS_bit]: begin
|
||||
`show_state("fetch_regs");
|
||||
// instr was just updated -> input register ids also
|
||||
// input registers available at next cycle
|
||||
state <= EXECUTE;
|
||||
error_latched <= decoderError;
|
||||
end
|
||||
state[EXECUTE_bit]: begin
|
||||
`show_state("execute");
|
||||
|
||||
// input registers are read, aluOut is up to date
|
||||
|
||||
// Looked-ahead instr.
|
||||
nextInstr <= mem_rdata;
|
||||
|
||||
// Needed for LOAD,STORE,jump,branch
|
||||
// (in other cases it will be ignored)
|
||||
addressReg <= aluOut;
|
||||
|
||||
if(error_latched) begin
|
||||
state <= ERROR;
|
||||
end else if(isLoad) begin
|
||||
state <= LOAD;
|
||||
PC <= PCplus4;
|
||||
end else begin
|
||||
(* parallel_case, full_case *)
|
||||
case(1'b1)
|
||||
isJump: begin
|
||||
PC <= aluOut[31:2];
|
||||
state <= WAIT_INSTR;
|
||||
end
|
||||
isBranch: begin
|
||||
if(predOut) begin
|
||||
PC <= aluOut[31:2];
|
||||
state <= WAIT_INSTR;
|
||||
end else begin
|
||||
PC <= PCplus4;
|
||||
state <= USE_PREFETCHED_INSTR;
|
||||
end
|
||||
end
|
||||
default: begin // linear execution flow
|
||||
PC <= PCplus4;
|
||||
state <= needWaitALU ? WAIT_ALU_OR_DATA : USE_PREFETCHED_INSTR;
|
||||
end
|
||||
endcase
|
||||
end
|
||||
end
|
||||
state[LOAD_bit]: begin
|
||||
`show_state("load");
|
||||
// data address (aluOut) was just updated
|
||||
// data ready at next cycle
|
||||
// we go to WAIT_ALU_OR_DATA to write back read data
|
||||
state <= WAIT_ALU_OR_DATA;
|
||||
end
|
||||
state[WAIT_ALU_OR_DATA_bit]: begin
|
||||
`show_state("wait_alu_or_data");
|
||||
// - If ALU is still busy, continue to wait.
|
||||
// - register writeback is active
|
||||
state <= aluBusy ? WAIT_ALU_OR_DATA : USE_PREFETCHED_INSTR;
|
||||
end
|
||||
state[ERROR_bit]: begin
|
||||
`bench($display("ERROR"));
|
||||
state <= ERROR;
|
||||
end
|
||||
default: begin
|
||||
`bench($display("UNKNOWN STATE"));
|
||||
state <= ERROR;
|
||||
end
|
||||
endcase
|
||||
end
|
||||
|
||||
/*********************************************************************/
|
||||
|
||||
`define show_opcode(opcode) `verbose($display("%x: %s",{PC,2'b00},opcode))
|
||||
|
||||
`ifdef BENCH
|
||||
always @(posedge clk) begin
|
||||
if(state[FETCH_REGS_bit]) begin
|
||||
case(instr[6:0])
|
||||
7'b0110111: `show_opcode("LUI");
|
||||
7'b0010111: `show_opcode("AUIPC");
|
||||
7'b1101111: `show_opcode("JAL");
|
||||
7'b1100111: `show_opcode("JALR");
|
||||
7'b1100011: `show_opcode("BRANCH");
|
||||
7'b0010011: `show_opcode("ALU reg imm");
|
||||
7'b0110011: `show_opcode("ALU reg reg");
|
||||
7'b0000011: `show_opcode("LOAD");
|
||||
7'b0100011: `show_opcode("STORE");
|
||||
7'b0001111: `show_opcode("FENCE");
|
||||
7'b1110011: `show_opcode("SYSTEM");
|
||||
endcase // case (instr[6:0])
|
||||
end // if (state[EXECUTE_bit])
|
||||
end
|
||||
`endif
|
||||
|
||||
endmodule
|
||||
Reference in New Issue
Block a user