split out TestIssuer into separate module
authorLuke Kenneth Casson Leighton <lkcl@lkcl.net>
Wed, 17 Jun 2020 11:17:31 +0000 (12:17 +0100)
committerLuke Kenneth Casson Leighton <lkcl@lkcl.net>
Wed, 17 Jun 2020 11:17:31 +0000 (12:17 +0100)
src/soc/simple/core.py
src/soc/simple/issuer.py [new file with mode: 0644]
src/soc/simple/test/test_issuer.py

index cb234628e2d512b964753a79aadb47b1957126b2..837e1d06481011ae77c82d6575058ab156a0a830 100644 (file)
@@ -31,8 +31,6 @@ from soc.decoder.power_decoder import create_pdecode
 from soc.decoder.power_decoder2 import PowerDecode2
 from soc.decoder.decode2execute1 import Data
 from soc.experiment.l0_cache import TstL0CacheBuffer # test only
-from soc.experiment.testmem import TestMemory # test only for instructions
-from soc.regfile.regfiles import FastRegs
 import operator
 
 
@@ -41,6 +39,7 @@ import operator
 def ortreereduce(tree, attr="data_o"):
     return treereduce(tree, operator.or_, lambda x: getattr(x, attr))
 
+
 # helper function to place full regs declarations first
 def sort_fuspecs(fuspecs):
     res = []
@@ -310,131 +309,8 @@ class NonProductionCore(Elaboratable):
         return list(self)
 
 
-class TestIssuer(Elaboratable):
-    """TestIssuer - reads instructions from TestMemory and issues them
-
-    efficiency and speed is not the main goal here: functional correctness is.
-    """
-    def __init__(self, addrwid=6, idepth=6):
-        # main instruction core
-        self.core = core = NonProductionCore(addrwid)
-
-        # Test Instruction memory
-        self.imem = TestMemory(32, idepth)
-        self.i_rd = self.imem.rdport
-        #self.i_wr = self.imem.write_port() errr...
-
-        # instruction go/monitor
-        self.go_insn_i = Signal(reset_less=True)
-        self.pc_o = Signal(64, reset_less=True)
-        self.pc_i = Data(64, "pc") # set "ok" to indicate "please change me"
-        self.busy_o = core.busy_o
-        self.memerr_o = Signal(reset_less=True)
-
-        # FAST regfile read /write ports
-        self.fast_rd1 = self.core.regs.rf['fast'].r_ports['d_rd1']
-        self.fast_wr1 = self.core.regs.rf['fast'].w_ports['d_wr1']
-
-    def elaborate(self, platform):
-        m = Module()
-        comb, sync = m.d.comb, m.d.sync
-
-        m.submodules.core = core = self.core
-        m.submodules.imem = imem = self.imem
-
-        # temporary hack: says "go" immediately for both address gen and ST
-        l0 = core.l0
-        ldst = core.fus.fus['ldst0']
-        m.d.comb += ldst.ad.go.eq(ldst.ad.rel) # link addr-go direct to rel
-        m.d.comb += ldst.st.go.eq(ldst.st.rel) # link store-go direct to rel
-
-        # 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)
-        comb += self.pc_o.eq(current_pc)
-        ilatch = Signal(32)
-
-        # next instruction (+4 on current)
-        nia = Signal(64, reset_less=True)
-        comb += nia.eq(current_pc + 4)
-
-        # temporaries
-        core_busy_o = core.busy_o         # core is busy
-        core_ivalid_i = core.ivalid_i     # instruction is valid
-        core_issue_i = core.issue_i       # instruction is issued
-        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"):
-                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.i_rd.addr.eq(pc[2:]) # ignore last 2 bits
-                    comb += current_insn.eq(self.i_rd.data)
-                    comb += current_pc.eq(pc)
-                    m.next = "INSN_READ" # move to "issue" phase
-
-            # got the instruction: start issue
-            with m.State("INSN_READ"):
-                comb += current_insn.eq(self.i_rd.data)
-                comb += core_ivalid_i.eq(1) # say instruction is valid
-                comb += core_issue_i.eq(1)  # and issued (ivalid_i 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(~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
-                    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
-
-    def __iter__(self):
-        yield from self.pc_i.ports()
-        yield self.pc_o
-        yield self.go_insn_i
-        yield self.memerr_o
-        yield from self.core.ports()
-        yield from self.imem.ports()
-
-    def ports(self):
-        return list(self)
-
-
 if __name__ == '__main__':
     dut = TestIssuer()
     vl = rtlil.convert(dut, ports=dut.ports())
     with open("test_issuer.il", "w") as f:
         f.write(vl)
-
-    dut = NonProductionCore()
-    vl = rtlil.convert(dut, ports=dut.ports())
-    with open("non_production_core.il", "w") as f:
-        f.write(vl)
-
diff --git a/src/soc/simple/issuer.py b/src/soc/simple/issuer.py
new file mode 100644 (file)
index 0000000..62c3b98
--- /dev/null
@@ -0,0 +1,150 @@
+"""simple core issuer
+
+not in any way intended for production use.  this runs a FSM that:
+
+* reads the Program Counter from FastRegs
+* reads an instruction from a fixed-size Test Memory
+* issues it to the Simple Core
+* waits for it to complete
+* increments the PC
+* does it all over again
+
+the purpose of this module is to verify the functional correctness
+of the Function Units in the absolute simplest and clearest possible
+way, and to at provide something that can be further incrementally
+improved.
+"""
+
+from nmigen import Elaboratable, Module, Signal
+from nmigen.cli import rtlil
+
+
+from soc.decoder.decode2execute1 import Data
+from soc.experiment.testmem import TestMemory # test only for instructions
+from soc.regfile.regfiles import FastRegs
+from soc.simple.core import NonProductionCore
+
+
+class TestIssuer(Elaboratable):
+    """TestIssuer - reads instructions from TestMemory and issues them
+
+    efficiency and speed is not the main goal here: functional correctness is.
+    """
+    def __init__(self, addrwid=6, idepth=6):
+        # main instruction core
+        self.core = core = NonProductionCore(addrwid)
+
+        # Test Instruction memory
+        self.imem = TestMemory(32, idepth)
+        self.i_rd = self.imem.rdport
+        #self.i_wr = self.imem.write_port() errr...
+
+        # instruction go/monitor
+        self.go_insn_i = Signal(reset_less=True)
+        self.pc_o = Signal(64, reset_less=True)
+        self.pc_i = Data(64, "pc") # set "ok" to indicate "please change me"
+        self.busy_o = core.busy_o
+        self.memerr_o = Signal(reset_less=True)
+
+        # FAST regfile read /write ports
+        self.fast_rd1 = self.core.regs.rf['fast'].r_ports['d_rd1']
+        self.fast_wr1 = self.core.regs.rf['fast'].w_ports['d_wr1']
+
+    def elaborate(self, platform):
+        m = Module()
+        comb, sync = m.d.comb, m.d.sync
+
+        m.submodules.core = core = self.core
+        m.submodules.imem = imem = self.imem
+
+        # temporary hack: says "go" immediately for both address gen and ST
+        l0 = core.l0
+        ldst = core.fus.fus['ldst0']
+        m.d.comb += ldst.ad.go.eq(ldst.ad.rel) # link addr-go direct to rel
+        m.d.comb += ldst.st.go.eq(ldst.st.rel) # link store-go direct to rel
+
+        # 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)
+        comb += self.pc_o.eq(current_pc)
+        ilatch = Signal(32)
+
+        # next instruction (+4 on current)
+        nia = Signal(64, reset_less=True)
+        comb += nia.eq(current_pc + 4)
+
+        # temporaries
+        core_busy_o = core.busy_o         # core is busy
+        core_ivalid_i = core.ivalid_i     # instruction is valid
+        core_issue_i = core.issue_i       # instruction is issued
+        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"):
+                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.i_rd.addr.eq(pc[2:]) # ignore last 2 bits
+                    comb += current_insn.eq(self.i_rd.data)
+                    comb += current_pc.eq(pc)
+                    m.next = "INSN_READ" # move to "issue" phase
+
+            # got the instruction: start issue
+            with m.State("INSN_READ"):
+                comb += current_insn.eq(self.i_rd.data)
+                comb += core_ivalid_i.eq(1) # say instruction is valid
+                comb += core_issue_i.eq(1)  # and issued (ivalid_i 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(~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
+                    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
+
+    def __iter__(self):
+        yield from self.pc_i.ports()
+        yield self.pc_o
+        yield self.go_insn_i
+        yield self.memerr_o
+        yield from self.core.ports()
+        yield from self.imem.ports()
+
+    def ports(self):
+        return list(self)
+
+
+if __name__ == '__main__':
+    dut = TestIssuer()
+    vl = rtlil.convert(dut, ports=dut.ports())
+    with open("test_issuer.il", "w") as f:
+        f.write(vl)
+
index c13d793df209c376c2debaf22eaeb0c7b502fcb5..b3a50c0a9c311bb19ac63d01a44ba9ea78f2b86a 100644 (file)
@@ -14,7 +14,7 @@ from soc.decoder.isa.all import ISA
 from soc.decoder.power_enums import Function, XER_bits
 
 
-from soc.simple.core import TestIssuer
+from soc.simple.issuer import TestIssuer
 from soc.experiment.compalu_multi import find_ok # hack
 
 from soc.simple.test.test_core import (setup_regs, check_regs,