add core start/stop capability, and OP_ATTN support
authorLuke Kenneth Casson Leighton <lkcl@lkcl.net>
Tue, 7 Jul 2020 13:14:46 +0000 (14:14 +0100)
committerLuke Kenneth Casson Leighton <lkcl@lkcl.net>
Tue, 7 Jul 2020 13:14:46 +0000 (14:14 +0100)
src/soc/simple/core.py
src/soc/simple/issuer.py
src/soc/simple/test/test_issuer.py

index faaee95632abb88bb19010df25e29f5242f67fac..f2e1a1de55e8295a04a48bcf9038489ddf63e604 100644 (file)
@@ -78,6 +78,11 @@ class NonProductionCore(Elaboratable):
         self.bigendian_i = self.pdecode2.dec.bigendian
         self.raw_opcode_i = self.pdecode2.dec.raw_opcode_in
 
+        # start/stop and terminated signalling
+        self.core_start_i = Signal(reset_less=True)
+        self.core_stop_i = Signal(reset_less=True)
+        self.core_terminated_o = Signal(reset=1) # indicates stopped
+
     def elaborate(self, platform):
         m = Module()
 
@@ -88,13 +93,24 @@ class NonProductionCore(Elaboratable):
         regs = self.regs
         fus = self.fus.fus
 
-        fu_bitdict = self.connect_instruction(m)
+        # core start/stopped state
+        core_stopped = Signal(reset=1) # begins in stopped state
+
+        # start/stop signalling
+        with m.If(self.core_start_i):
+            m.d.sync += core_stopped.eq(1)
+        with m.If(self.core_stop_i):
+            m.d.sync += core_stopped.eq(0)
+        m.d.comb += self.core_terminated_o.eq(core_stopped)
+
+        # connect up Function Units, then read/write ports
+        fu_bitdict = self.connect_instruction(m, core_stopped)
         self.connect_rdports(m, fu_bitdict)
         self.connect_wrports(m, fu_bitdict)
 
         return m
 
-    def connect_instruction(self, m):
+    def connect_instruction(self, m, core_stopped):
         comb, sync = m.d.comb, m.d.sync
         fus = self.fus.fus
         dec2 = self.pdecode2
@@ -105,19 +121,30 @@ class NonProductionCore(Elaboratable):
         for i, funame in enumerate(fus.keys()):
             fu_bitdict[funame] = fu_enable[i]
 
+        # only run when allowed and when instruction is valid
+        can_run = Signal(reset_less=True)
+        comb += can_run.eq(self.ivalid_i & ~core_stopped)
+
         # connect up instructions.  only one is enabled at any given time
         for funame, fu in fus.items():
             fnunit = fu.fnunit.value
             enable = Signal(name="en_%s" % funame, reset_less=True)
-            comb += enable.eq(self.ivalid_i &
-                             (dec2.e.do.fn_unit & fnunit).bool())
+            comb += enable.eq((dec2.e.do.fn_unit & fnunit).bool() & can_run)
+
+            # run this FunctionUnit if enabled, except if the instruction
+            # is "attn" in which case we HALT.
             with m.If(enable):
-                comb += fu.oper_i.eq_from_execute1(dec2.e)
-                comb += fu.issue_i.eq(self.issue_i)
-                comb += self.busy_o.eq(fu.busy_o)
-                rdmask = dec2.rdflags(fu)
-                comb += fu.rdmaskn.eq(~rdmask)
-            comb += fu_bitdict[funame].eq(enable)
+                with m.If(dec2.e.op.internal_op == InternalOp.OP_ATTN):
+                    # check for ATTN: halt if true
+                    m.d.sync += core_stopped.eq(1)
+                with m.Else():
+                    # route operand, issue, busy, read flags and mask to FU
+                    comb += fu.oper_i.eq_from_execute1(dec2.e)
+                    comb += fu.issue_i.eq(self.issue_i)
+                    comb += self.busy_o.eq(fu.busy_o)
+                    rdmask = dec2.rdflags(fu)
+                    comb += fu.rdmaskn.eq(~rdmask)
+                    comb += fu_bitdict[funame].eq(enable)
 
         return fu_bitdict
 
index e787df5f55190276508a057eb971b437cc15ccfe..d0d1c5e2350263207e41e7b22bcfd2c79e798fbb 100644 (file)
@@ -70,14 +70,14 @@ class TestIssuer(Elaboratable):
 
         # PC and instruction from I-Memory
         current_insn = Signal(32) # current fetched instruction (note sync)
-        current_pc = Signal(64) # current PC (note it is reset/sync)
+        cur_pc = Signal(64) # current PC (note it is reset/sync)
         pc_changed = Signal() # note write to PC
-        comb += self.pc_o.eq(current_pc)
+        comb += self.pc_o.eq(cur_pc)
         ilatch = Signal(32)
 
         # next instruction (+4 on current)
         nia = Signal(64, reset_less=True)
-        comb += nia.eq(current_pc + 4)
+        comb += nia.eq(cur_pc + 4)
 
         # temporaries
         core_busy_o = core.busy_o         # core is busy
@@ -86,65 +86,65 @@ class TestIssuer(Elaboratable):
         core_be_i = core.bigendian_i      # bigendian mode
         core_opcode_i = core.raw_opcode_i # raw opcode
 
-        # actually use a nmigen FSM for the first time (w00t)
-        with m.FSM() as fsm:
-
-            # waiting (zzz)
-            with m.State("IDLE"):
-                sync += pc_changed.eq(0)
-                with m.If(self.go_insn_i):
-                    # instruction allowed to go: start by reading the PC
-                    pc = Signal(64, reset_less=True)
-                    with m.If(self.pc_i.ok):
-                        # incoming override (start from pc_i)
-                        comb += pc.eq(self.pc_i.data)
+        # only run if not in halted state
+        with m.If(~core.core_terminated_o):
+
+            # actually use a nmigen FSM for the first time (w00t)
+            with m.FSM() as fsm:
+
+                # waiting (zzz)
+                with m.State("IDLE"):
+                    sync += pc_changed.eq(0)
+                    with m.If(self.go_insn_i):
+                        # instruction allowed to go: start by reading the PC
+                        pc = Signal(64, reset_less=True)
+                        with m.If(self.pc_i.ok):
+                            # incoming override (start from pc_i)
+                            comb += pc.eq(self.pc_i.data)
+                        with m.Else():
+                            # otherwise read FastRegs regfile for PC
+                            comb += self.fast_rd1.ren.eq(1<<FastRegs.PC)
+                            comb += pc.eq(self.fast_rd1.data_o)
+                        # capture the PC and also drop it into Insn Memory
+                        # we have joined a pair of combinatorial memory
+                        # lookups together.  this is Generally Bad.
+                        comb += self.imem.a_pc_i.eq(pc)
+                        comb += self.imem.a_valid_i.eq(1)
+                        comb += self.imem.f_valid_i.eq(1)
+                        sync += cur_pc.eq(pc)
+                        m.next = "INSN_READ" # move to "wait for bus" phase
+
+                # waiting for instruction bus (stays there until not busy)
+                with m.State("INSN_READ"):
+                    with m.If(self.imem.f_busy_o): # zzz...
+                        # busy: stay in wait-read
+                        comb += self.imem.a_valid_i.eq(1)
+                        comb += self.imem.f_valid_i.eq(1)
                     with m.Else():
-                        # otherwise read FastRegs regfile for PC
-                        comb += self.fast_rd1.ren.eq(1<<FastRegs.PC)
-                        comb += pc.eq(self.fast_rd1.data_o)
-                    # capture the PC and also drop it into Insn Memory
-                    # we have joined a pair of combinatorial memory
-                    # lookups together.  this is Generally Bad.
-                    comb += self.imem.a_pc_i.eq(pc)
-                    comb += self.imem.a_valid_i.eq(1)
-                    comb += self.imem.f_valid_i.eq(1)
-                    sync += current_pc.eq(pc)
-                    m.next = "INSN_READ" # move to "wait for bus" phase
-
-            # waiting for instruction bus (stays there until not busy)
-            with m.State("INSN_READ"):
-                with m.If(self.imem.f_busy_o): # zzz...
-                    # busy: stay in wait-read
-                    comb += self.imem.a_valid_i.eq(1)
-                    comb += self.imem.f_valid_i.eq(1)
-                with m.Else():
-                    # not busy: instruction fetched
-                    insn = self.imem.f_instr_o.word_select(current_pc[2], 32)
-                    comb += current_insn.eq(insn)
+                        # not busy: instruction fetched
+                        insn = self.imem.f_instr_o.word_select(cur_pc[2], 32)
+                        comb += current_insn.eq(insn)
+                        comb += core_ivalid_i.eq(1) # say instruction is valid
+                        comb += core_issue_i.eq(1)  # and issued 
+                        comb += core_be_i.eq(0)     # little-endian mode
+                        comb += core_opcode_i.eq(current_insn) # actual opcode
+                        sync += ilatch.eq(current_insn)
+                        m.next = "INSN_ACTIVE" # move to "wait completion" 
+
+                # instruction started: must wait till it finishes
+                with m.State("INSN_ACTIVE"):
                     comb += core_ivalid_i.eq(1) # say instruction is valid
-                    comb += core_issue_i.eq(1)  # and issued (ivalid redundant)
-                    comb += core_be_i.eq(0)     # little-endian mode
-                    comb += core_opcode_i.eq(current_insn) # actual opcode
-                    sync += ilatch.eq(current_insn)
-                    m.next = "INSN_ACTIVE" # move to "wait for completion" phase
-
-            # instruction started: must wait till it finishes
-            with m.State("INSN_ACTIVE"):
-                comb += core_ivalid_i.eq(1) # say instruction is valid
-                comb += core_opcode_i.eq(ilatch) # actual opcode
-                #sync += core_issue_i.eq(0) # issue raises for only one cycle
-                with m.If(self.fast_nia.wen):
-                    sync += pc_changed.eq(1)
-                with m.If(~core_busy_o): # instruction done!
-                    #sync += core_ivalid_i.eq(0) # say instruction is invalid
-                    #sync += core_opcode_i.eq(0) # clear out (no good reason)
-                    # ok here we are not reading the branch unit.  TODO
-                    # this just blithely overwrites whatever pipeline updated
-                    # the PC
-                    with m.If(~pc_changed):
-                        comb += self.fast_wr1.wen.eq(1<<FastRegs.PC)
-                        comb += self.fast_wr1.data_i.eq(nia)
-                    m.next = "IDLE" # back to idle
+                    comb += core_opcode_i.eq(ilatch) # actual opcode
+                    with m.If(self.fast_nia.wen):
+                        sync += pc_changed.eq(1)
+                    with m.If(~core_busy_o): # instruction done!
+                        # ok here we are not reading the branch unit.  TODO
+                        # this just blithely overwrites whatever pipeline
+                        # updated the PC
+                        with m.If(~pc_changed):
+                            comb += self.fast_wr1.wen.eq(1<<FastRegs.PC)
+                            comb += self.fast_wr1.data_i.eq(nia)
+                        m.next = "IDLE" # back to idle
 
         return m
 
index ec81aa7a279d676d56c0628e6bbfe6c9b99ec335..1551ccfa9842eba4539c715a55e39b6ae999c691 100644 (file)
@@ -79,6 +79,11 @@ class TestRunner(FHDLTestCase):
         pdecode2 = core.pdecode2
         l0 = core.l0
 
+        # get core going
+        yield core.core_start_i.eq(1)
+        yield
+        yield core.core_start_i.eq(0)
+
         comb += issuer.pc_i.data.eq(pc_i)
         comb += issuer.go_insn_i.eq(go_insn_i)
 
@@ -155,13 +160,13 @@ if __name__ == "__main__":
     unittest.main(exit=False)
     suite = unittest.TestSuite()
     suite.addTest(TestRunner(GeneralTestCases.test_data))
-    suite.addTest(TestRunner(LDSTTestCase.test_data))
-    suite.addTest(TestRunner(CRTestCase.test_data))
-    suite.addTest(TestRunner(ShiftRotTestCase.test_data))
-    suite.addTest(TestRunner(LogicalTestCase.test_data))
-    suite.addTest(TestRunner(ALUTestCase.test_data))
-    suite.addTest(TestRunner(BranchTestCase.test_data))
-    suite.addTest(TestRunner(SPRTestCase.test_data))
+    #suite.addTest(TestRunner(LDSTTestCase.test_data))
+    #suite.addTest(TestRunner(CRTestCase.test_data))
+    #suite.addTest(TestRunner(ShiftRotTestCase.test_data))
+    #suite.addTest(TestRunner(LogicalTestCase.test_data))
+    #suite.addTest(TestRunner(ALUTestCase.test_data))
+    #suite.addTest(TestRunner(BranchTestCase.test_data))
+    #suite.addTest(TestRunner(SPRTestCase.test_data))
 
     runner = unittest.TextTestRunner()
     runner.run(suite)