// 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