""" /* * Copyright 2018 Jacob Lifshay * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * */ `timescale 1ns / 100ps """ from migen import * from migen.fhdl import verilog from riscvdefs import * from cpudefs import * class CPUDecoder(Module): """ decodes a 32-bit instruction into an immediate and other constituent parts, including the opcode and funct3 and funct7, followed by a further (hierarchical) breakdown of the action required to be taken. unidentified actions are decoded as an illegal instruction trap. """ def __init__(self): self.instruction = Signal(32) self.funct7 = Signal(7) self.funct3 = Signal(3) self.rd = Signal(5) self.rs1 = Signal(5) self.rs2 = Signal(5) self.immediate = Signal(32) self.opcode = Signal(7) self.decode_action = Signal(decode_action) # decode bits of instruction self.comb += self.funct7.eq(self.instruction[25:32]) self.comb += self.funct3.eq(self.instruction[12:15]) self.comb += self.rd.eq (self.instruction[7:12]) self.comb += self.rs1.eq (self.instruction[15:20]) self.comb += self.rs2.eq (self.instruction[20:25]) self.comb += self.opcode.eq(self.instruction[0:7]) # add combinatorial decode opcode case statements for immed and action self.comb += self.calculate_immediate() self.comb += self.calculate_action() def calculate_immediate(self): """ calculate immediate """ ci = {} no_imm = Constant(0x0, 32) # R-type: no immediate for op in [OP.amo, OP.op, OP.op_32, OP.op_fp]: ci[op] = self.immediate.eq(no_imm) # I-type: sign-extended bits 20-31 im = Cat(self.instruction[20:], Replicate(self.instruction[31], 20)) for op in [OP.load, OP.load_fp, OP.misc_mem, OP.op_imm, OP.op_imm_32, OP.jalr, OP.system]: ci[op] = self.immediate.eq(im) # S-type im = Cat(self.instruction[7:12], self.instruction[25:31], Replicate(self.instruction[31], 21)) for op in [OP.store, OP.store_fp]: ci[op] = self.immediate.eq(im) # B-type im = Cat(Constant(0, 1), self.instruction[8:12], self.instruction[25:31], self.instruction[7], Replicate(self.instruction[31], 20)) for op in [OP.branch, ]: ci[op] = self.immediate.eq(im) # U-type im = Cat(Constant(0, 1), self.instruction[12:], ) for op in [OP.auipc, OP.lui]: ci[op] = self.immediate.eq(im) # J-type im = Cat(Constant(0, 1), self.instruction[21:25], self.instruction[25:31], self.instruction[20], self.instruction[12:20], Replicate(self.instruction[31], 12)) for op in [OP.jal, ]: ci[op] = self.immediate.eq(im) # R4-type: no immediate for op in [OP.madd, OP.msub, OP.nmsub, OP.nmadd]: ci[op] = self.immediate.eq(no_imm) # unknown for op in [ OP.custom_0, OP.op_48b_escape_0, OP.custom_1, OP.op_64b_escape, OP.reserved_10101, OP.rv128_0, OP.op_48b_escape_1, OP.reserved_11010, OP.reserved_11101, OP.rv128_1, OP.op_80b_escape]: ci[op] = self.immediate.eq(no_imm) # default for op in [ "default", ]: ci[op] = self.immediate.eq(no_imm) return Case(self.opcode, ci) def _decode_funct3(self, action, options): """ decode by list of cases """ c = {} # load opcode for op in options: c[op] = self.decode_action.eq(action) # default c["default"] = self.decode_action.eq(DA.trap_illegal_instruction) return Case(self.funct3, c) def calculate_store_action(self): """ decode store action """ return self._decode_funct3(DA.store, [F3.sb, F3.sh, F3.sw, ]) def calculate_load_action(self): """ decode load action """ return self._decode_funct3(DA.load, [F3.lb, F3.lbu, F3.lh, F3.lhu, F3.lw, ]) def calculate_branch_action(self): """ decode branch action """ return self._decode_funct3(DA.branch, [F3.beq, F3.bne, F3.blt, F3.bge, F3.bltu, F3.bgeu ]) def calculate_jalr_action(self): """ decode jalr action """ return self._decode_funct3(DA.jalr, [F3.jalr, ]) def calculate_op_action(self): """ decode op action: the arith ops, and, or, add, xor, sr/sl etc. """ c = {} immz = Constant(0, 12) regz = Constant(0, 5) # slli c[F3.slli] = \ If((self.funct7 == Constant(0, 7)), self.decode_action.eq(DA.op_op_imm) ).Else( self.decode_action.eq(DA.trap_illegal_instruction)) # srli/srai c[F3.srli_srai] = \ If((self.funct7 == Constant(0, 7) | \ (self.funct7 == Constant(0x20, 7))), self.decode_action.eq(DA.op_op_imm) ).Else( self.decode_action.eq(DA.trap_illegal_instruction)) # default c["default"] = self.decode_action.eq(DA.op_op_imm) return Case(self.funct3, c) def calculate_misc_action(self): """ decode misc mem action: fence and fence_i """ c = {} immz = Constant(0, 12) regz = Constant(0, 5) # fence c[F3.fence] = \ If((self.immediate[8:12] == immz) & (self.rs1 == regz) & \ (self.rd == regz), self.decode_action.eq(DA.fence) ).Else( self.decode_action.eq(DA.trap_illegal_instruction)) # fence.i c[F3.fence_i] = \ If((self.immediate[0:12] == immz) & (self.rs1 == regz) & \ (self.rd == regz), self.decode_action.eq(DA.fence_i) ).Else( self.decode_action.eq(DA.trap_illegal_instruction)) # default c["default"] = self.decode_action.eq(DA.trap_illegal_instruction) return Case(self.funct3, c) def calculate_system_action(self): """ decode opcode system: ebreak and csrs """ c = {} b1 = Constant(1, 32) regz = Constant(0, 5) # ebreak c[F3.ecall_ebreak] = \ If((self.immediate == ~b1) & (self.rs1 == regz) & \ (self.rd == regz), self.decode_action.eq(DA.trap_ecall_ebreak) ).Else( self.decode_action.eq(DA.trap_illegal_instruction)) # csrs for op in [ F3.csrrw, F3.csrrs, F3.csrrc, F3.csrrwi, F3.csrrsi, F3.csrrci]: c[op] = self.decode_action.eq(DA.csr) # default c["default"] = self.decode_action.eq(DA.trap_illegal_instruction) return Case(self.funct3, c) def calculate_action(self): """ calculate action based on opcode. this is a first level case statement that calls down to 2nd level case (and in some cases if logic) mostly using funct3 (funct7 in the case of arith ops). """ c = {} c[OP.load ] = self.calculate_load_action() c[OP.misc_mem] = self.calculate_misc_action() c[OP.op_imm ] = self.calculate_op_action() c[OP.op ] = self.calculate_op_action() c[OP.lui ] = self.decode_action.eq(DA.lui_auipc) c[OP.auipc ] = self.decode_action.eq(DA.lui_auipc) c[OP.store ] = self.calculate_store_action() c[OP.branch ] = self.calculate_branch_action() c[OP.jalr ] = self.calculate_jalr_action() c[OP.jal ] = self.decode_action.eq(DA.jal) c[OP.system ] = self.calculate_system_action() # big batch of unrecognised opcodes: throw trap. for o in [ OP.load_fp, OP.custom_0, OP.op_imm_32, OP.op_48b_escape_0, OP.store_fp, OP.custom_1, OP.amo, OP.op_32, OP.op_64b_escape, OP.madd, OP.msub, OP.nmsub, OP.nmadd, OP.op_fp, OP.reserved_10101, OP.rv128_0, OP.op_48b_escape_1, OP.reserved_11010, OP.reserved_11101, OP.rv128_1, OP.op_80b_escape, "default", ]: c[o] = self.decode_action.eq(DA.trap_illegal_instruction) return Case(self.opcode, c) if __name__ == "__main__": example = CPUDecoder() print(verilog.convert(example, { example.instruction, example.funct7, example.funct3, example.rd, example.rs1, example.rs2, example.immediate, example.opcode, example.decode_action, }))