add start of instruction queue
authorLuke Kenneth Casson Leighton <lkcl@lkcl.net>
Wed, 29 May 2019 15:11:32 +0000 (16:11 +0100)
committerLuke Kenneth Casson Leighton <lkcl@lkcl.net>
Wed, 29 May 2019 15:11:32 +0000 (16:11 +0100)
src/experiment/score6600.py
src/scoreboard/instruction_q.py [new file with mode: 0644]

index 42404736d248b52f019d36173dacb47242950173..d9144826c73ee3408152833088efb0f1f3006419 100644 (file)
@@ -804,7 +804,7 @@ def scoreboard_sim(dut, alusim):
         for i in range(1, dut.n_regs):
             val = randint(0, (1<<alusim.rwidth)-1)
             #val = 31+i*3
-            val = i
+            #val = i
             yield dut.intregs.regs[i].reg.eq(val)
             alusim.setval(i, val)
 
diff --git a/src/scoreboard/instruction_q.py b/src/scoreboard/instruction_q.py
new file mode 100644 (file)
index 0000000..94056e5
--- /dev/null
@@ -0,0 +1,145 @@
+from nmigen.compat.sim import run_simulation
+from nmigen.cli import verilog, rtlil
+from nmigen import Module, Signal, Cat, Array, Const, Repl, Elaboratable
+from nmutil.iocontrol import RecordObject
+from nmutil.nmoperator import eq
+
+
+class Instruction(RecordObject):
+    def __init__(self, name, wid, opwid):
+        RecordObject.__init__(self, name=name)
+        self.oper_i = Signal(opwid, reset_less=True)
+        self.dest_i = Signal(wid, reset_less=True)
+        self.src1_i = Signal(wid, reset_less=True)
+        self.src2_i = Signal(wid, reset_less=True)
+
+    @staticmethod
+    def nq(n_insns, name, wid, opwid):
+        q = []
+        for i in range(n_insns):
+            q.append(Instruction("%s%d" % (name, i), wid, opwid))
+        return Array(q)
+
+
+class InstructionQ(Elaboratable):
+    """ contains a queue of (part-decoded) instructions.
+
+        it is expected that the user of this queue will simply
+        inspect the queue contents directly, indicating at the start
+        of each clock cycle how many need to be removed.
+    """
+    def __init__(self, wid, opwid, iqlen, n_in, n_out):
+        """ constructor
+
+            Inputs
+
+            * :wid:         register file width
+            * :opwid:       operand width
+            * :iqlen:       instruction queue length
+            * :n_in:        max number of instructions allowed "in"
+        """
+        self.reg_width = wid
+        self.opwid = opwid
+        self.n_in = n_in
+        self.n_out = n_out
+
+        self.q = Instruction.nq(iqlen, "i", wid, opwid)
+        self.qlen_o = Signal(max=iqlen)
+
+        self.p_add_i = Signal(max=n_in) # instructions to add (from data_i)
+        self.p_ready_o = Signal() # instructions were added
+        self.data_i = Instruction.nq(n_in, "data_i", wid, opwid)
+        
+        self.data_o = Instruction.nq(n_out, "data_o", wid, opwid)
+        self.n_sub_i = Signal(max=n_out) # number of instructions to remove
+        self.n_sub_o = Signal(max=n_out) # number of instructions removed
+
+    def elaborate(self, platform):
+        m = Module()
+        comb = m.d.comb
+        sync = m.d.sync
+
+        iqlen = len(self.q)
+        mqlen = Const(iqlen, iqlen*2)
+
+        start_copy = Signal(max=iqlen*2)
+        end_copy = Signal(max=iqlen*2)
+
+        # work out how many can be subtracted from the queue
+        with m.If(self.n_sub_i >= self.qlen_o):
+            comb += self.n_sub_o.eq(self.qlen_o)
+        with m.Elif(self.n_sub_i):
+            comb += self.n_sub_o.eq(self.n_sub_i)
+
+        # work out the start and end of where data can be written
+        comb += start_copy.eq(self.qlen_o - self.n_sub_o)
+        comb += end_copy.eq(start_copy + self.p_add_i - 1)
+        comb += self.p_ready_o.eq(end_copy < self.qlen_o) # ready if room exists
+
+        # this is going to be _so_ expensive in terms of gates... *sigh*...
+        with m.If(self.p_ready_o):
+            for i in range(iqlen):
+                cfrom = Signal(max=iqlen*2)
+                cto = Signal(max=iqlen*2)
+                comb += cfrom.eq(Const(i, iqlen+1) + start_copy)
+                comb += cto.eq(Const(i, iqlen+1) + end_copy)
+                with m.If((cfrom < mqlen) & (cto < mqlen)):
+                    sync += self.q[cto].oper_i.eq(self.q[cfrom].oper_i)
+                    sync += self.q[cto].dest_i.eq(self.q[cfrom].dest_i)
+                    sync += self.q[cto].src1_i.eq(self.q[cfrom].src1_i)
+                    sync += self.q[cto].src2_i.eq(self.q[cfrom].src2_i)
+
+        return m
+
+    def __iter__(self):
+        for o in self.q:
+            yield from list(o)
+        yield self.qlen_o
+
+        yield self.p_ready_o
+        for o in self.data_i:
+            yield from list(o)
+        yield self.p_add_i
+        
+        for o in self.data_o:
+            yield from list(o)
+        yield self.n_sub_i
+        yield self.n_sub_o
+
+    def ports(self):
+        return list(self)
+
+
+def instruction_q_sim(dut):
+    yield dut.dest_i.eq(1)
+    yield dut.issue_i.eq(1)
+    yield
+    yield dut.issue_i.eq(0)
+    yield
+    yield dut.src1_i.eq(1)
+    yield dut.issue_i.eq(1)
+    yield
+    yield
+    yield
+    yield dut.issue_i.eq(0)
+    yield
+    yield dut.go_rd_i.eq(1)
+    yield
+    yield dut.go_rd_i.eq(0)
+    yield
+    yield dut.go_wr_i.eq(1)
+    yield
+    yield dut.go_wr_i.eq(0)
+    yield
+
+def test_instruction_q():
+    dut = InstructionQ(16, 4, 4, n_in=2, n_out=2)
+    vl = rtlil.convert(dut, ports=dut.ports())
+    with open("test_instruction_q.il", "w") as f:
+        f.write(vl)
+
+    run_simulation(dut, instruction_q_sim(dut),
+                   vcd_name='test_instruction_q.vcd')
+
+if __name__ == '__main__':
+    test_instruction_q()