Merge branch 'master' of ssh://git.libre-riscv.org:922/rv32
[rv32.git] / cpu_decoder.py
diff --git a/cpu_decoder.py b/cpu_decoder.py
new file mode 100644 (file)
index 0000000..c92fb88
--- /dev/null
@@ -0,0 +1,273 @@
+"""
+/*
+ * 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,
+           }))