ibex/zeroriscy_decoder.sv
Pasquale Davide Schiavone edf308df16 SolderPad Header
2017-08-03 15:12:13 +02:00

590 lines
21 KiB
Systemverilog

// Copyright 2017 ETH Zurich and University of Bologna.
// Copyright and related rights are licensed under the Solderpad Hardware
// License, Version 0.51 (the “License”); you may not use this file except in
// compliance with the License. You may obtain a copy of the License at
// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law
// or agreed to in writing, software, hardware and materials distributed under
// this License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
////////////////////////////////////////////////////////////////////////////////
// Engineer Andreas Traber - atraber@iis.ee.ethz.ch //
// //
// Additional contributions by: //
// Matthias Baer - baermatt@student.ethz.ch //
// Igor Loi - igor.loi@unibo.it //
// Sven Stucki - svstucki@student.ethz.ch //
// Davide Schiavone - pschiavo@iis.ee.ethz.ch //
// Markus Wegmann - markus.wegmann@technokrat.ch //
// //
// Design Name: Decoder //
// Project Name: zero-riscy //
// Language: SystemVerilog //
// //
// Description: Decoder //
// //
////////////////////////////////////////////////////////////////////////////////
`include "zeroriscy_config.sv"
import zeroriscy_defines::*;
module zeroriscy_decoder
#(
parameter RV32M = 1
)
(
// singals running to/from controller
input logic deassert_we_i, // deassert we, we are stalled or not active
input logic data_misaligned_i, // misaligned data load/store in progress
input logic branch_mux_i,
input logic jump_mux_i,
output logic illegal_insn_o, // illegal instruction encountered
output logic ebrk_insn_o, // trap instruction encountered
output logic mret_insn_o, // return from exception instruction encountered
output logic ecall_insn_o, // environment call (syscall) instruction encountered
output logic pipe_flush_o, // pipeline flush is requested
// from IF/ID pipeline
input logic [31:0] instr_rdata_i, // instruction read from instr memory/cache
input logic illegal_c_insn_i, // compressed instruction decode failed
// ALU signals
output logic [ALU_OP_WIDTH-1:0] alu_operator_o, // ALU operation selection
output logic [2:0] alu_op_a_mux_sel_o, // operand a selection: reg value, PC, immediate or zero
output logic [2:0] alu_op_b_mux_sel_o, // oNOperand b selection: reg value or immediate
output logic [0:0] imm_a_mux_sel_o, // immediate selection for operand a
output logic [3:0] imm_b_mux_sel_o, // immediate selection for operand b
// MUL, DIV related control signals
output logic mult_int_en_o, // perform integer multiplication
output logic div_int_en_o, // perform integer division or reminder
output logic [1:0] multdiv_operator_o,
output logic [1:0] multdiv_signed_mode_o,
// register file related signals
output logic regfile_we_o, // write enable for regfile
// CSR manipulation
output logic csr_access_o, // access to CSR
output logic [1:0] csr_op_o, // operation to perform on CSR
output logic csr_status_o, // access to xstatus CSR
// LD/ST unit signals
output logic data_req_o, // start transaction to data memory
output logic data_we_o, // data memory write enable
output logic [1:0] data_type_o, // data type on data memory: byte, half word or word
output logic data_sign_extension_o, // sign extension on read data from data memory
output logic [1:0] data_reg_offset_o, // offset in byte inside register for stores
output logic data_load_event_o, // data request is in the special event range
// jump/branches
output logic jump_in_id_o, // jump is being calculated in ALU
output logic branch_in_id_o
);
// write enable/request control
logic regfile_we;
logic data_req;
logic mult_int_en;
logic div_int_en;
logic branch_in_id;
logic jump_in_id;
logic [1:0] csr_op;
logic csr_illegal;
/////////////////////////////////////////////
// ____ _ //
// | _ \ ___ ___ ___ __| | ___ _ __ //
// | | | |/ _ \/ __/ _ \ / _` |/ _ \ '__| //
// | |_| | __/ (_| (_) | (_| | __/ | //
// |____/ \___|\___\___/ \__,_|\___|_| //
// //
/////////////////////////////////////////////
always_comb
begin
jump_in_id = 1'b0;
branch_in_id = 1'b0;
alu_operator_o = ALU_SLTU;
alu_op_a_mux_sel_o = OP_A_REGA_OR_FWD;
alu_op_b_mux_sel_o = OP_B_REGB_OR_FWD;
imm_a_mux_sel_o = IMMA_ZERO;
imm_b_mux_sel_o = IMMB_I;
mult_int_en = 1'b0;
div_int_en = 1'b0;
multdiv_operator_o = MD_OP_MULL;
multdiv_signed_mode_o = 2'b00;
regfile_we = 1'b0;
csr_access_o = 1'b0;
csr_status_o = 1'b0;
csr_illegal = 1'b0;
csr_op = CSR_OP_NONE;
data_we_o = 1'b0;
data_type_o = 2'b00;
data_sign_extension_o = 1'b0;
data_reg_offset_o = 2'b00;
data_req = 1'b0;
data_load_event_o = 1'b0;
illegal_insn_o = 1'b0;
ebrk_insn_o = 1'b0;
mret_insn_o = 1'b0;
ecall_insn_o = 1'b0;
pipe_flush_o = 1'b0;
unique case (instr_rdata_i[6:0])
//////////////////////////////////////
// _ _ _ __ __ ____ ____ //
// | | | | | \/ | _ \/ ___| //
// _ | | | | | |\/| | |_) \___ \ //
// | |_| | |_| | | | | __/ ___) | //
// \___/ \___/|_| |_|_| |____/ //
// //
//////////////////////////////////////
OPCODE_JAL: begin // Jump and Link
jump_in_id = 1'b1;
if(jump_mux_i) begin
// Calculate jump target
alu_op_a_mux_sel_o = OP_A_CURRPC;
alu_op_b_mux_sel_o = OP_B_IMM;
imm_b_mux_sel_o = IMMB_UJ;
alu_operator_o = ALU_ADD;
regfile_we = 1'b0;
end else begin
// Calculate and store PC+4
alu_op_a_mux_sel_o = OP_A_CURRPC;
alu_op_b_mux_sel_o = OP_B_IMM;
imm_b_mux_sel_o = IMMB_PCINCR;
alu_operator_o = ALU_ADD;
regfile_we = 1'b1;
end
end
OPCODE_JALR: begin // Jump and Link Register
jump_in_id = 1'b1;
if(jump_mux_i) begin
// Calculate jump target
alu_op_a_mux_sel_o = OP_A_REGA_OR_FWD;
alu_op_b_mux_sel_o = OP_B_IMM;
imm_b_mux_sel_o = IMMB_I;
alu_operator_o = ALU_ADD;
regfile_we = 1'b0;
end else begin
// Calculate and store PC+4
alu_op_a_mux_sel_o = OP_A_CURRPC;
alu_op_b_mux_sel_o = OP_B_IMM;
imm_b_mux_sel_o = IMMB_PCINCR;
alu_operator_o = ALU_ADD;
regfile_we = 1'b1;
end
if (instr_rdata_i[14:12] != 3'b0) begin
jump_in_id = 1'b0;
regfile_we = 1'b0;
illegal_insn_o = 1'b1;
end
end
OPCODE_BRANCH: begin // Branch
branch_in_id = 1'b1;
if (branch_mux_i)
begin
unique case (instr_rdata_i[14:12])
3'b000: alu_operator_o = ALU_EQ;
3'b001: alu_operator_o = ALU_NE;
3'b100: alu_operator_o = ALU_LTS;
3'b101: alu_operator_o = ALU_GES;
3'b110: alu_operator_o = ALU_LTU;
3'b111: alu_operator_o = ALU_GEU;
default: begin
illegal_insn_o = 1'b1;
end
endcase
end
else begin
// Calculate jump target in EX
alu_op_a_mux_sel_o = OP_A_CURRPC;
alu_op_b_mux_sel_o = OP_B_IMM;
imm_b_mux_sel_o = IMMB_SB;
alu_operator_o = ALU_ADD;
regfile_we = 1'b0;
end
end
//////////////////////////////////
// _ ____ ______ _____ //
// | | | _ \ / / ___|_ _| //
// | | | | | |/ /\___ \ | | //
// | |___| |_| / / ___) || | //
// |_____|____/_/ |____/ |_| //
// //
//////////////////////////////////
OPCODE_STORE: begin
data_req = 1'b1;
data_we_o = 1'b1;
alu_operator_o = ALU_ADD;
if (instr_rdata_i[14] == 1'b0) begin
// offset from immediate
imm_b_mux_sel_o = IMMB_S;
alu_op_b_mux_sel_o = OP_B_IMM;
end
// Register offset is illegal since no register c available
else begin
data_req = 1'b0;
data_we_o = 1'b0;
illegal_insn_o = 1'b1;
end
// store size
unique case (instr_rdata_i[13:12])
2'b00: data_type_o = 2'b10; // SB
2'b01: data_type_o = 2'b01; // SH
2'b10: data_type_o = 2'b00; // SW
default: begin
data_req = 1'b0;
data_we_o = 1'b0;
illegal_insn_o = 1'b1;
end
endcase
end
OPCODE_LOAD: begin
data_req = 1'b1;
regfile_we = 1'b1;
data_type_o = 2'b00;
// offset from immediate
alu_operator_o = ALU_ADD;
alu_op_b_mux_sel_o = OP_B_IMM;
imm_b_mux_sel_o = IMMB_I;
// sign/zero extension
data_sign_extension_o = ~instr_rdata_i[14];
// load size
unique case (instr_rdata_i[13:12])
2'b00: data_type_o = 2'b10; // LB
2'b01: data_type_o = 2'b01; // LH
2'b10: data_type_o = 2'b00; // LW
default: data_type_o = 2'b00; // illegal or reg-reg
endcase
// reg-reg load (different encoding)
if (instr_rdata_i[14:12] == 3'b111) begin
// offset from RS2
alu_op_b_mux_sel_o = OP_B_REGB_OR_FWD;
// sign/zero extension
data_sign_extension_o = ~instr_rdata_i[30];
// load size
unique case (instr_rdata_i[31:25])
7'b0000_000,
7'b0100_000: data_type_o = 2'b10; // LB, LBU
7'b0001_000,
7'b0101_000: data_type_o = 2'b01; // LH, LHU
7'b0010_000: data_type_o = 2'b00; // LW
default: begin
illegal_insn_o = 1'b1;
end
endcase
end
// special p.elw (event load)
if (instr_rdata_i[14:12] == 3'b110)
data_load_event_o = 1'b1;
if (instr_rdata_i[14:12] == 3'b011) begin
// LD -> RV64 only
illegal_insn_o = 1'b1;
end
end
//////////////////////////
// _ _ _ _ //
// / \ | | | | | | //
// / _ \ | | | | | | //
// / ___ \| |__| |_| | //
// /_/ \_\_____\___/ //
// //
//////////////////////////
OPCODE_LUI: begin // Load Upper Immediate
alu_op_a_mux_sel_o = OP_A_IMM;
alu_op_b_mux_sel_o = OP_B_IMM;
imm_a_mux_sel_o = IMMA_ZERO;
imm_b_mux_sel_o = IMMB_U;
alu_operator_o = ALU_ADD;
regfile_we = 1'b1;
end
OPCODE_AUIPC: begin // Add Upper Immediate to PC
alu_op_a_mux_sel_o = OP_A_CURRPC;
alu_op_b_mux_sel_o = OP_B_IMM;
imm_b_mux_sel_o = IMMB_U;
alu_operator_o = ALU_ADD;
regfile_we = 1'b1;
end
OPCODE_OPIMM: begin // Register-Immediate ALU Operations
alu_op_b_mux_sel_o = OP_B_IMM;
imm_b_mux_sel_o = IMMB_I;
regfile_we = 1'b1;
unique case (instr_rdata_i[14:12])
3'b000: alu_operator_o = ALU_ADD; // Add Immediate
3'b010: alu_operator_o = ALU_SLTS; // Set to one if Lower Than Immediate
3'b011: alu_operator_o = ALU_SLTU; // Set to one if Lower Than Immediate Unsigned
3'b100: alu_operator_o = ALU_XOR; // Exclusive Or with Immediate
3'b110: alu_operator_o = ALU_OR; // Or with Immediate
3'b111: alu_operator_o = ALU_AND; // And with Immediate
3'b001: begin
alu_operator_o = ALU_SLL; // Shift Left Logical by Immediate
if (instr_rdata_i[31:25] != 7'b0)
illegal_insn_o = 1'b1;
end
3'b101: begin
if (instr_rdata_i[31:25] == 7'b0)
alu_operator_o = ALU_SRL; // Shift Right Logical by Immediate
else if (instr_rdata_i[31:25] == 7'b010_0000)
alu_operator_o = ALU_SRA; // Shift Right Arithmetically by Immediate
else
illegal_insn_o = 1'b1;
end
default: illegal_insn_o = 1'b1;
endcase
end
OPCODE_OP: begin // Register-Register ALU operation
regfile_we = 1'b1;
if (instr_rdata_i[31]) begin
illegal_insn_o = 1'b1;
end
else
begin // non bit-manipulation instructions
if (~instr_rdata_i[28])
unique case ({instr_rdata_i[30:25], instr_rdata_i[14:12]})
// RV32I ALU operations
{6'b00_0000, 3'b000}: alu_operator_o = ALU_ADD; // Add
{6'b10_0000, 3'b000}: alu_operator_o = ALU_SUB; // Sub
{6'b00_0000, 3'b010}: alu_operator_o = ALU_SLTS; // Set Lower Than
{6'b00_0000, 3'b011}: alu_operator_o = ALU_SLTU; // Set Lower Than Unsigned
{6'b00_0000, 3'b100}: alu_operator_o = ALU_XOR; // Xor
{6'b00_0000, 3'b110}: alu_operator_o = ALU_OR; // Or
{6'b00_0000, 3'b111}: alu_operator_o = ALU_AND; // And
{6'b00_0000, 3'b001}: alu_operator_o = ALU_SLL; // Shift Left Logical
{6'b00_0000, 3'b101}: alu_operator_o = ALU_SRL; // Shift Right Logical
{6'b10_0000, 3'b101}: alu_operator_o = ALU_SRA; // Shift Right Arithmetic
// supported RV32M instructions
{6'b00_0001, 3'b000}: begin // mul
alu_operator_o = ALU_ADD;
multdiv_operator_o = MD_OP_MULL;
mult_int_en = 1'b1;
multdiv_signed_mode_o = 2'b00;
illegal_insn_o = RV32M ? 1'b0 : 1'b1;
end
{6'b00_0001, 3'b001}: begin // mulh
alu_operator_o = ALU_ADD;
multdiv_operator_o = MD_OP_MULH;
mult_int_en = 1'b1;
multdiv_signed_mode_o = 2'b11;
illegal_insn_o = RV32M ? 1'b0 : 1'b1;
end
{6'b00_0001, 3'b010}: begin // mulhsu
alu_operator_o = ALU_ADD;
multdiv_operator_o = MD_OP_MULH;
mult_int_en = 1'b1;
multdiv_signed_mode_o = 2'b01;
illegal_insn_o = RV32M ? 1'b0 : 1'b1;
end
{6'b00_0001, 3'b011}: begin // mulhu
alu_operator_o = ALU_ADD;
multdiv_operator_o = MD_OP_MULH;
mult_int_en = 1'b1;
multdiv_signed_mode_o = 2'b00;
illegal_insn_o = RV32M ? 1'b0 : 1'b1;
end
{6'b00_0001, 3'b100}: begin // div
alu_operator_o = ALU_ADD;
multdiv_operator_o = MD_OP_DIV;
div_int_en = 1'b1;
multdiv_signed_mode_o = 2'b11;
illegal_insn_o = RV32M ? 1'b0 : 1'b1;
end
{6'b00_0001, 3'b101}: begin // divu
alu_operator_o = ALU_ADD;
multdiv_operator_o = MD_OP_DIV;
div_int_en = 1'b1;
multdiv_signed_mode_o = 2'b00;
illegal_insn_o = RV32M ? 1'b0 : 1'b1;
end
{6'b00_0001, 3'b110}: begin // rem
alu_operator_o = ALU_ADD;
multdiv_operator_o = MD_OP_REM;
div_int_en = 1'b1;
multdiv_signed_mode_o = 2'b11;
illegal_insn_o = RV32M ? 1'b0 : 1'b1;
end
{6'b00_0001, 3'b111}: begin // remu
alu_operator_o = ALU_ADD;
multdiv_operator_o = MD_OP_REM;
div_int_en = 1'b1;
multdiv_signed_mode_o = 2'b00;
illegal_insn_o = RV32M ? 1'b0 : 1'b1;
end
default: begin
illegal_insn_o = 1'b1;
end
endcase
end
end
////////////////////////////////////////////////
// ____ ____ _____ ____ ___ _ _ //
// / ___|| _ \| ____/ ___|_ _| / \ | | //
// \___ \| |_) | _|| | | | / _ \ | | //
// ___) | __/| |__| |___ | | / ___ \| |___ //
// |____/|_| |_____\____|___/_/ \_\_____| //
// //
////////////////////////////////////////////////
OPCODE_SYSTEM: begin
if (instr_rdata_i[14:12] == 3'b000)
begin
// non CSR related SYSTEM instructions
unique case (instr_rdata_i[31:20])
12'h000: // ECALL
begin
// environment (system) call
ecall_insn_o = 1'b1;
end
12'h001: // ebreak
begin
// debugger trap
ebrk_insn_o = 1'b1;
end
12'h302: // mret
begin
mret_insn_o = 1'b1;
end
12'h105: // wfi
begin
// flush pipeline
pipe_flush_o = 1'b1;
end
default:
begin
illegal_insn_o = 1'b1;
end
endcase
end
else
begin
// instruction to read/modify CSR
csr_access_o = 1'b1;
regfile_we = 1'b1;
alu_op_b_mux_sel_o = OP_B_IMM;
imm_a_mux_sel_o = IMMA_Z;
imm_b_mux_sel_o = IMMB_I; // CSR address is encoded in I imm
if (instr_rdata_i[14] == 1'b1) begin
// rs1 field is used as immediate
alu_op_a_mux_sel_o = OP_A_IMM;
end else begin
alu_op_a_mux_sel_o = OP_A_REGA_OR_FWD;
end
unique case (instr_rdata_i[13:12])
2'b01: csr_op = CSR_OP_WRITE;
2'b10: csr_op = CSR_OP_SET;
2'b11: csr_op = CSR_OP_CLEAR;
default: csr_illegal = 1'b1;
endcase
if(~csr_illegal)
if (instr_rdata_i[31:20] == 12'h300)
//access to mstatus
csr_status_o = 1'b1;
illegal_insn_o = csr_illegal;
end
end
default: begin
illegal_insn_o = 1'b1;
end
endcase
// make sure invalid compressed instruction causes an exception
if (illegal_c_insn_i) begin
illegal_insn_o = 1'b1;
end
// misaligned access was detected by the LSU
// TODO: this section should eventually be moved out of the decoder
if (data_misaligned_i == 1'b1)
begin
// only part of the pipeline is unstalled, make sure that the
// correct operands are sent to the AGU
alu_op_a_mux_sel_o = OP_A_REGA_OR_FWD;
alu_op_b_mux_sel_o = OP_B_IMM;
imm_b_mux_sel_o = IMMB_PCINCR;
// if prepost increments are used, we do not write back the
// second address since the first calculated address was
// the correct one
regfile_we = 1'b0;
end
end
// deassert we signals (in case of stalls)
assign regfile_we_o = (deassert_we_i) ? 1'b0 : regfile_we;
assign mult_int_en_o = RV32M ? ((deassert_we_i) ? 1'b0 : mult_int_en) : 1'b0;
assign div_int_en_o = RV32M ? ((deassert_we_i) ? 1'b0 : div_int_en ) : 1'b0;
assign data_req_o = (deassert_we_i) ? 1'b0 : data_req;
assign csr_op_o = (deassert_we_i) ? CSR_OP_NONE : csr_op;
assign jump_in_id_o = (deassert_we_i) ? 1'b0 : jump_in_id;
assign branch_in_id_o = (deassert_we_i) ? 1'b0 : branch_in_id;
endmodule // controller