--- /dev/null
+"""
+/*
+ * 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,
+ }))