189 lines
7.5 KiB
Verilog
189 lines
7.5 KiB
Verilog
/********************* 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
|
|
|