experiment morphing ALU to take subset of Decode2ToExecute1
authorLuke Kenneth Casson Leighton <lkcl@lkcl.net>
Thu, 9 Apr 2020 12:57:29 +0000 (13:57 +0100)
committerLuke Kenneth Casson Leighton <lkcl@lkcl.net>
Thu, 9 Apr 2020 12:57:31 +0000 (13:57 +0100)
src/soc/experiment/alu_hier.py
src/soc/experiment/compalu.py

index 27331dfd204c87233b6b1523f68e261a166f2804..9fd21c493a486199e09d02cd97d18e816570ff98 100644 (file)
@@ -10,22 +10,92 @@ only one cycle (sync)
 """
 
 from nmigen import Elaboratable, Signal, Module, Const, Mux
 """
 
 from nmigen import Elaboratable, Signal, Module, Const, Mux
+from nmigen.hdl.rec import Record, Layout
 from nmigen.cli import main
 from nmigen.cli import verilog, rtlil
 from nmigen.cli import main
 from nmigen.cli import verilog, rtlil
-from soc.decoder.power_enums import InternalOp
+from nmigen.compat.sim import run_simulation
+
+from soc.decoder.power_enums import InternalOp, CryIn
 
 import operator
 
 
 
 import operator
 
 
+class CompALUOpSubset(Record):
+    """CompALUOpSubset
+
+    a copy of the relevant subset information from Decode2Execute1Type
+    needed for ALU operations.
+    """
+    def __init__(self):
+        layout = (('insn_type', InternalOp),
+                  ('nia', 64),
+                  ('imm_data', Layout((("imm", 64), ("imm_ok", 1)))),
+                    #'cr = Signal(32, reset_less=True) # NO: this is from the CR SPR
+                    #'xerc = XerBits() # NO: this is from the XER SPR
+                  ('lk', 1),
+                  ('rc', Layout((("rc", 1), ("rc_ok", 1)))),
+                  ('oe', Layout((("oe", 1), ("oe_ok", 1)))),
+                  ('invert_a', 1),
+                  ('invert_out', 1),
+                  ('input_carry', CryIn),
+                  ('output_carry', 1),
+                  ('input_cr', 1),
+                  ('output_cr', 1),
+                  ('is_32bit', 1),
+                  ('is_signed', 1),
+                  ('byte_reverse', 1),
+                  ('sign_extend', 1))
+
+        Record.__init__(self, Layout(layout))
+
+        # grrr.  Record does not have kwargs
+        self.insn_type.reset_less = True
+        self.nia.reset_less = True
+        #self.cr = Signal(32, reset_less = True
+        #self.xerc = XerBits(
+        self.lk.reset_less = True
+        self.invert_a.reset_less = True
+        self.invert_out.reset_less = True
+        self.input_carry.reset_less = True
+        self.output_carry.reset_less = True
+        self.input_cr.reset_less = True
+        self.output_cr.reset_less = True
+        self.is_32bit.reset_less = True
+        self.is_signed.reset_less = True
+        self.byte_reverse.reset_less = True
+        self.sign_extend.reset_less = True
+
+    def ports(self):
+        return [self.insn_type,
+                self.nia,
+                #self.cr,
+                #self.xerc,
+                self.lk,
+                self.invert_a,
+                self.invert_out,
+                self.input_carry,
+                self.output_carry,
+                self.input_cr,
+                self.output_cr,
+                self.is_32bit,
+                self.is_signed,
+                self.byte_reverse,
+                self.sign_extend,
+        ]
+
 class Adder(Elaboratable):
     def __init__(self, width):
 class Adder(Elaboratable):
     def __init__(self, width):
+        self.invert_a = Signal()
         self.a   = Signal(width)
         self.b   = Signal(width)
         self.o   = Signal(width)
 
     def elaborate(self, platform):
         m = Module()
         self.a   = Signal(width)
         self.b   = Signal(width)
         self.o   = Signal(width)
 
     def elaborate(self, platform):
         m = Module()
-        m.d.comb += self.o.eq(self.a + self.b)
+        with m.If(self.invert_a):
+            m.d.comb += self.o.eq((~self.a) + self.b)
+        with m.Else():
+            m.d.comb += self.o.eq(self.a + self.b)
         return m
 
 
         return m
 
 
@@ -75,7 +145,7 @@ class ALU(Elaboratable):
         self.n_ready_i = Signal()
         self.n_valid_o = Signal()
         self.counter   = Signal(4)
         self.n_ready_i = Signal()
         self.n_valid_o = Signal()
         self.counter   = Signal(4)
-        self.op  = Signal(InternalOp)
+        self.op  = CompALUOpSubset()
         self.a   = Signal(width)
         self.b   = Signal(width)
         self.o   = Signal(width)
         self.a   = Signal(width)
         self.b   = Signal(width)
         self.o   = Signal(width)
@@ -84,21 +154,23 @@ class ALU(Elaboratable):
     def elaborate(self, platform):
         m = Module()
         add = Adder(self.width)
     def elaborate(self, platform):
         m = Module()
         add = Adder(self.width)
-        sub = Subtractor(self.width)
         mul = Multiplier(self.width)
         shf = Shifter(self.width)
 
         m.submodules.add = add
         mul = Multiplier(self.width)
         shf = Shifter(self.width)
 
         m.submodules.add = add
-        m.submodules.sub = sub
         m.submodules.mul = mul
         m.submodules.shf = shf
 
         # really should not activate absolutely all ALU inputs like this
         m.submodules.mul = mul
         m.submodules.shf = shf
 
         # really should not activate absolutely all ALU inputs like this
-        for mod in [add, sub, mul, shf]:
+        for mod in [add, mul, shf]:
             m.d.comb += [
                 mod.a.eq(self.a),
                 mod.b.eq(self.b),
             ]
             m.d.comb += [
                 mod.a.eq(self.a),
                 mod.b.eq(self.b),
             ]
+
+        # pass invert (and carry later)
+        m.d.comb += add.invert_a.eq(self.op.invert_a)
+
         go_now = Signal(reset_less=True) # testing no-delay ALU
 
         with m.If(self.p_valid_i):
         go_now = Signal(reset_less=True) # testing no-delay ALU
 
         with m.If(self.p_valid_i):
@@ -108,26 +180,28 @@ class ALU(Elaboratable):
                 m.d.sync += self.p_ready_o.eq(1)
 
                 # as this is a "fake" pipeline, just grab the output right now
                 m.d.sync += self.p_ready_o.eq(1)
 
                 # as this is a "fake" pipeline, just grab the output right now
-                with m.If(self.op == InternalOp.OP_ADD):
+                with m.If(self.op.insn_type == InternalOp.OP_ADD):
                     m.d.sync += self.o.eq(add.o)
                     m.d.sync += self.o.eq(add.o)
-                with m.Elif(self.op == InternalOp.OP_MUL_L64):
+                with m.Elif(self.op.insn_type == InternalOp.OP_MUL_L64):
                     m.d.sync += self.o.eq(mul.o)
                     m.d.sync += self.o.eq(mul.o)
-                with m.Elif(self.op == InternalOp.OP_SHR):
+                with m.Elif(self.op.insn_type == InternalOp.OP_SHR):
                     m.d.sync += self.o.eq(shf.o)
                 # TODO: SUB
 
                     m.d.sync += self.o.eq(shf.o)
                 # TODO: SUB
 
-                with m.Switch(self.op):
-                    for i, mod in enumerate([add, sub, mul, shf]):
-                        with m.Case(i):
-                            m.d.sync += self.o.eq(mod.o)
-                with m.If(self.op == 2): # MUL, to take 5 instructions
+                # NOTE: all of these are fake, just something to test
+
+                # MUL, to take 5 instructions
+                with m.If(self.op.insn_type == InternalOp.OP_MUL_L64.value):
                     m.d.sync += self.counter.eq(5)
                     m.d.sync += self.counter.eq(5)
-                with m.Elif(self.op == 3): # SHIFT to take 7
+                # SHIFT to take 7
+                with m.Elif(self.op.insn_type == InternalOp.OP_SHR.value):
                     m.d.sync += self.counter.eq(7)
                     m.d.sync += self.counter.eq(7)
-                with m.Elif(self.op == 1): # SUB to take 1, straight away
+                # SUB to take 1, straight away
+                with m.If(self.op.insn_type == InternalOp.OP_ADD.value):
                     m.d.sync += self.counter.eq(1)
                     m.d.comb += go_now.eq(1)
                     m.d.sync += self.counter.eq(1)
                     m.d.comb += go_now.eq(1)
-                with m.Else(): # ADD to take 2
+                # ADD to take 2
+                with m.Else():
                     m.d.sync += self.counter.eq(2)
         with m.Else():
             # input says no longer valid, so drop ready as well.
                     m.d.sync += self.counter.eq(2)
         with m.Else():
             # input says no longer valid, so drop ready as well.
@@ -151,7 +225,7 @@ class ALU(Elaboratable):
         return m
 
     def __iter__(self):
         return m
 
     def __iter__(self):
-        yield self.op
+        yield from self.op.ports()
         yield self.a
         yield self.b
         yield self.o
         yield self.a
         yield self.b
         yield self.o
@@ -247,13 +321,55 @@ class BranchALU(Elaboratable):
     def ports(self):
         return list(self)
 
     def ports(self):
         return list(self)
 
+def run_op(dut, a, b, op, inv_a=0):
+    yield dut.a.eq(a)
+    yield dut.b.eq(b)
+    yield dut.op.insn_type.eq(op)
+    yield dut.op.invert_a.eq(inv_a)
+    yield dut.n_ready_i.eq(0)
+    yield dut.p_valid_i.eq(1)
+    yield
+    while True:
+        yield
+        n_valid_o = yield dut.n_valid_o
+        if n_valid_o:
+            break
+    yield
 
 
-if __name__ == "__main__":
+    result = yield dut.o
+    yield dut.p_valid_i.eq(0)
+    yield dut.n_ready_i.eq(0)
+    yield
+
+    return result
+
+
+def alu_sim(dut):
+    result = yield from run_op(dut, 5, 3, InternalOp.OP_ADD)
+    print ("alu_sim add", result)
+    assert (result == 8)
+
+    result = yield from run_op(dut, 2, 3, InternalOp.OP_MUL_L64)
+    print ("alu_sim mul", result)
+    assert (result == 6)
+
+    result = yield from run_op(dut, 5, 3, InternalOp.OP_ADD, inv_a=1)
+    print ("alu_sim add-inv", result)
+    assert (result == 65533)
+
+
+def test_alu():
     alu = ALU(width=16)
     alu = ALU(width=16)
+    run_simulation(alu, alu_sim(alu), vcd_name='test_alusim.vcd')
+
     vl = rtlil.convert(alu, ports=alu.ports())
     with open("test_alu.il", "w") as f:
         f.write(vl)
 
     vl = rtlil.convert(alu, ports=alu.ports())
     with open("test_alu.il", "w") as f:
         f.write(vl)
 
+
+if __name__ == "__main__":
+    test_alu()
+
     alu = BranchALU(width=16)
     vl = rtlil.convert(alu, ports=alu.ports())
     with open("test_branch_alu.il", "w") as f:
     alu = BranchALU(width=16)
     vl = rtlil.convert(alu, ports=alu.ports())
     with open("test_branch_alu.il", "w") as f:
index 55e2799fe75414af93310b1d3445e2db6e6f04f7..9fcced738ed1ffa9dfe9f529a32809f8392d2a3d 100644 (file)
@@ -6,6 +6,7 @@ from nmutil.latch import SRLatch, latchregister
 from soc.decoder.power_decoder2 import Data
 from soc.decoder.power_enums import InternalOp
 
 from soc.decoder.power_decoder2 import Data
 from soc.decoder.power_enums import InternalOp
 
+from alu_hier import CompALUOpSubset
 
 """ Computation Unit (aka "ALU Manager").
 
 
 """ Computation Unit (aka "ALU Manager").
 
@@ -39,13 +40,9 @@ from soc.decoder.power_enums import InternalOp
       register is placed combinatorially onto the output, and (2) the
       req_l latch is cleared, busy is dropped, and the Comp Unit is back
       through its revolving door to do another task.
       register is placed combinatorially onto the output, and (2) the
       req_l latch is cleared, busy is dropped, and the Comp Unit is back
       through its revolving door to do another task.
-
-    Notes on oper_i:
-
-    * bits[0:2] are for the ALU, add=0, sub=1, shift=2, mul=3
-    * bit[2] are the immediate (bit[2]=1 == immediate mode)
 """
 
 """
 
+
 class ComputationUnitNoDelay(Elaboratable):
     def __init__(self, rwid, e, alu):
         self.rwid = rwid
 class ComputationUnitNoDelay(Elaboratable):
     def __init__(self, rwid, e, alu):
         self.rwid = rwid
@@ -60,8 +57,8 @@ class ComputationUnitNoDelay(Elaboratable):
         self.go_die_i = Signal() # go die (reset)
 
         # operation / data input
         self.go_die_i = Signal() # go die (reset)
 
         # operation / data input
-        self.oper_i = e.insn_type    # operand
-        self.imm_i =  e.imm_data      # immediate in
+        self.oper_i = CompALUOpSubset() # operand
+        self.imm_i =  self.oper_i.imm_data      # immediate in
         self.src1_i = Signal(rwid, reset_less=True) # oper1 in
         self.src2_i = Signal(rwid, reset_less=True) # oper2 in
 
         self.src1_i = Signal(rwid, reset_less=True) # oper1 in
         self.src2_i = Signal(rwid, reset_less=True) # oper2 in