Fetch and decode the SVP64 prefix
[soc.git] / src / soc / simple / issuer.py
index 0d1fa1f22fc506ecf28dd2f29b698e57fd4b8d71..2526bcb888fb9fa64f4df3b8d0376e9dcba26ab7 100644 (file)
@@ -16,13 +16,13 @@ improved.
 """
 
 from nmigen import (Elaboratable, Module, Signal, ClockSignal, ResetSignal,
-                    ClockDomain, DomainRenamer)
+                    ClockDomain, DomainRenamer, Mux)
 from nmigen.cli import rtlil
 from nmigen.cli import main
 import sys
 
 from soc.decoder.power_decoder import create_pdecode
-from soc.decoder.power_decoder2 import PowerDecode2
+from soc.decoder.power_decoder2 import PowerDecode2, SVP64PrefixDecoder
 from soc.decoder.decode2execute1 import IssuerDecode2ToOperand
 from soc.decoder.decode2execute1 import Data
 from soc.experiment.testmem import TestMemory # test only for instructions
@@ -37,7 +37,8 @@ from soc.config.pinouts import get_pinspecs
 from soc.config.state import CoreState
 from soc.interrupts.xics import XICS_ICP, XICS_ICS
 from soc.bus.simple_gpio import SimpleGPIO
-from soc.clock.select import ClockSelect, DummyPLL
+from soc.clock.select import ClockSelect
+from soc.clock.dummypll import DummyPLL
 
 
 from nmutil.util import rising_edge
@@ -50,6 +51,22 @@ class TestIssuerInternal(Elaboratable):
     """
     def __init__(self, pspec):
 
+        # JTAG interface.  add this right at the start because if it's
+        # added it *modifies* the pspec, by adding enable/disable signals
+        # for parts of the rest of the core
+        self.jtag_en = hasattr(pspec, "debug") and pspec.debug == 'jtag'
+        if self.jtag_en:
+            subset = {'uart', 'mtwi', 'eint', 'gpio', 'mspi0', 'mspi1',
+                      'pwm', 'sd0', 'sdr'}
+            self.jtag = JTAG(get_pinspecs(subset=subset))
+            # add signals to pspec to enable/disable icache and dcache
+            # (or data and intstruction wishbone if icache/dcache not included)
+            # https://bugs.libre-soc.org/show_bug.cgi?id=520
+            # TODO: do we actually care if these are not domain-synchronised?
+            # honestly probably not.
+            pspec.wb_icache_en = self.jtag.wb_icache_en
+            pspec.wb_dcache_en = self.jtag.wb_dcache_en
+
         # add interrupt controller?
         self.xics = hasattr(pspec, "xics") and pspec.xics == True
         if self.xics:
@@ -71,6 +88,7 @@ class TestIssuerInternal(Elaboratable):
         self.cur_state = CoreState("cur") # current state (MSR/PC/EINT)
         self.pdecode2 = PowerDecode2(pdecode, state=self.cur_state,
                                      opkls=IssuerDecode2ToOperand)
+        self.svp64 = SVP64PrefixDecoder() # for decoding SVP64 prefix
 
         # Test Instruction memory
         self.imem = ConfigFetchUnit(pspec).fu
@@ -81,13 +99,6 @@ class TestIssuerInternal(Elaboratable):
         # DMI interface
         self.dbg = CoreDebug()
 
-        # JTAG interface
-        self.jtag_en = hasattr(pspec, "debug") and pspec.debug == 'jtag'
-        if self.jtag_en:
-            subset = {'uart', 'mtwi', 'eint', 'gpio', 'mspi0', 'mspi1',
-                      'pwm', 'sd0', 'sdr'}
-            self.jtag = JTAG(get_pinspecs(subset=subset))
-
         # instruction go/monitor
         self.pc_o = Signal(64, reset_less=True)
         self.pc_i = Data(64, "pc_i") # set "ok" to indicate "please change me"
@@ -148,6 +159,7 @@ class TestIssuerInternal(Elaboratable):
         # instruction decoder
         pdecode = create_pdecode()
         m.submodules.dec2 = pdecode2 = self.pdecode2
+        m.submodules.svp64 = svp64 = self.svp64
 
         # convenience
         dmi, d_reg, d_cr, d_xer, = dbg.dmi, dbg.d_gpr, dbg.d_cr, dbg.d_xer
@@ -165,7 +177,7 @@ class TestIssuerInternal(Elaboratable):
             m.d.por += delay.eq(delay - 1)
         comb += cd_por.clk.eq(ClockSignal())
 
-        # power-on reset delay 
+        # power-on reset delay
         core_rst = ResetSignal("coresync")
         comb += ti_rst.eq(delay != 0 | dbg.core_rst_o | ResetSignal())
         comb += core_rst.eq(ti_rst)
@@ -186,9 +198,9 @@ class TestIssuerInternal(Elaboratable):
         comb += self.pc_o.eq(cur_state.pc)
         ilatch = Signal(32)
 
-        # next instruction (+4 on current)
+        # address of the next instruction, in the absence of a branch
+        # depends on the instruction size
         nia = Signal(64, reset_less=True)
-        comb += nia.eq(cur_state.pc + 4)
 
         # read the PC
         pc = Signal(64, reset_less=True)
@@ -227,34 +239,41 @@ class TestIssuerInternal(Elaboratable):
 
         insn_type = core.e.do.insn_type
 
+        # handshake signals between fetch and decode/execute
+        # fetch FSM can run as soon as the PC is valid
+        fetch_pc_valid_i = Signal()
+        fetch_pc_ready_o = Signal()
+        # when done, deliver the instruction to the next FSM
+        fetch_insn_o = Signal(32, reset_less=True)
+        fetch_insn_valid_o = Signal()
+        fetch_insn_ready_i = Signal()
+
         # actually use a nmigen FSM for the first time (w00t)
         # this FSM is perhaps unusual in that it detects conditions
         # then "holds" information, combinatorially, for the core
         # (as opposed to using sync - which would be on a clock's delay)
         # this includes the actual opcode, valid flags and so on.
-        with m.FSM() as fsm:
+        with m.FSM(name='fetch_fsm'):
 
             # waiting (zzz)
             with m.State("IDLE"):
-                sync += pc_changed.eq(0)
-                sync += core.e.eq(0)
-                sync += core.raw_insn_i.eq(0)
-                sync += core.bigendian_i.eq(0)
                 with m.If(~dbg.core_stop_o & ~core_rst):
-                    # instruction allowed to go: start by reading the PC
-                    # 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_state.pc.eq(pc)
-
-                    # initiate read of MSR.  arrives one clock later
-                    comb += self.state_r_msr.ren.eq(1<<StateRegs.MSR)
-                    sync += msr_read.eq(0)
-
-                    m.next = "INSN_READ" # move to "wait for bus" phase
+                    comb += fetch_pc_ready_o.eq(1)
+                    with m.If(fetch_pc_valid_i):
+                        # instruction allowed to go: start by reading the PC
+                        # 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_state.pc.eq(pc)
+
+                        # initiate read of MSR.  arrives one clock later
+                        comb += self.state_r_msr.ren.eq(1 << StateRegs.MSR)
+                        sync += msr_read.eq(0)
+
+                        m.next = "INSN_READ"  # move to "wait for bus" phase
                 with m.Else():
                     comb += core.core_stopped_i.eq(1)
                     comb += dbg.core_stopped_i.eq(1)
@@ -276,7 +295,68 @@ class TestIssuerInternal(Elaboratable):
                         insn = f_instr_o
                     else:
                         insn = f_instr_o.word_select(cur_state.pc[2], 32)
-                    comb += dec_opcode_i.eq(insn) # actual opcode
+                    # decode the SVP64 prefix, if any
+                    comb += svp64.raw_opcode_in.eq(insn)
+                    comb += svp64.bigendian.eq(self.core_bigendian_i)
+                    # pass the decoded prefix (if any) to PowerDecoder2
+                    sync += pdecode2.sv_rm.eq(svp64.svp64_rm)
+                    # calculate the address of the following instruction
+                    insn_size = Mux(svp64.is_svp64_mode, 8, 4)
+                    sync += nia.eq(cur_state.pc + insn_size)
+                    with m.If(~svp64.is_svp64_mode):
+                        # with no prefix, store the instruction
+                        # and hand it directly to the next FSM
+                        sync += fetch_insn_o.eq(insn)
+                        m.next = "INSN_READY"
+                    with m.Else():
+                        # fetch the rest of the instruction from memory
+                        comb += self.imem.a_pc_i.eq(cur_state.pc + 4)
+                        comb += self.imem.a_valid_i.eq(1)
+                        comb += self.imem.f_valid_i.eq(1)
+                        m.next = "INSN_READ2"
+
+            with m.State("INSN_READ2"):
+                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
+                    f_instr_o = self.imem.f_instr_o
+                    if f_instr_o.width == 32:
+                        insn = f_instr_o
+                    else:
+                        insn = f_instr_o.word_select((cur_state.pc+4)[2], 32)
+                    sync += fetch_insn_o.eq(insn)
+                    m.next = "INSN_READY"
+
+            with m.State("INSN_READY"):
+                # hand over the instruction, to be decoded
+                comb += fetch_insn_valid_o.eq(1)
+                with m.If(fetch_insn_ready_i):
+                    m.next = "IDLE"
+
+        # decode / issue / execute FSM
+        with m.FSM():
+
+            # go fetch the instruction at the current PC
+            # at this point, there is no instruction running, that
+            # could inadvertently update the PC.
+            with m.State("INSN_FETCH"):
+                comb += fetch_pc_valid_i.eq(1)
+                with m.If(fetch_pc_ready_o):
+                    m.next = "INSN_WAIT"
+
+            # decode the instruction when it arrives
+            with m.State("INSN_WAIT"):
+                comb += fetch_insn_ready_i.eq(1)
+                with m.If(fetch_insn_valid_o):
+                    # decode the instruction
+                    # TODO, before issuing new instruction first
+                    # check if it's SVP64. (svp64.is_svp64_mode set)
+                    # if yes, record the svp64_rm, put that into
+                    # pdecode2.sv_rm, then read another 32 bits (INSN_FETCH2?)
+                    comb += dec_opcode_i.eq(fetch_insn_o)  # actual opcode
                     sync += core.e.eq(pdecode2.e)
                     sync += core.state.eq(cur_state)
                     sync += core.raw_insn_i.eq(dec_opcode_i)
@@ -289,6 +369,7 @@ class TestIssuerInternal(Elaboratable):
             with m.State("INSN_START"):
                 comb += core_ivalid_i.eq(1) # instruction is valid
                 comb += core_issue_i.eq(1)  # and issued
+                sync += pc_changed.eq(0)
 
                 m.next = "INSN_ACTIVE" # move to "wait completion"
 
@@ -308,7 +389,7 @@ class TestIssuerInternal(Elaboratable):
                     sync += core.e.eq(0)
                     sync += core.raw_insn_i.eq(0)
                     sync += core.bigendian_i.eq(0)
-                    m.next = "IDLE" # back to idle
+                    m.next = "INSN_FETCH"  # back to fetch
 
         # this bit doesn't have to be in the FSM: connect up to read
         # regfiles on demand from DMI
@@ -450,10 +531,11 @@ class TestIssuer(Elaboratable):
         self.ti = TestIssuerInternal(pspec)
 
         self.pll = DummyPLL()
-        self.clksel = ClockSelect()
 
         # PLL direct clock or not
-        self.pll_en = hasattr(pspec, "use_pll") and pspec.pll_en
+        self.pll_en = hasattr(pspec, "use_pll") and pspec.use_pll
+        if self.pll_en:
+            self.pll_18_o = Signal(reset_less=True)
 
     def elaborate(self, platform):
         m = Module()
@@ -463,50 +545,51 @@ class TestIssuer(Elaboratable):
         m.submodules.ti = ti = self.ti
         cd_int = ClockDomain("coresync")
 
-        # ClockSelect runs at PLL output internal clock rate
-        m.submodules.clksel = clksel = DomainRenamer("pllclk")(self.clksel)
-        m.submodules.pll = pll = self.pll
+        if self.pll_en:
+            # ClockSelect runs at PLL output internal clock rate
+            m.submodules.pll = pll = self.pll
+
+            # add clock domains from PLL
+            cd_pll = ClockDomain("pllclk")
+            m.domains += cd_pll
+
+            # PLL clock established.  has the side-effect of running clklsel
+            # at the PLL's speed (see DomainRenamer("pllclk") above)
+            pllclk = ClockSignal("pllclk")
+            comb += pllclk.eq(pll.clk_pll_o)
+
+            # wire up external 24mhz to PLL
+            comb += pll.clk_24_i.eq(ClockSignal())
+
+            # output 18 mhz PLL test signal
+            comb += self.pll_18_o.eq(pll.pll_18_o)
 
-        # add 2 clock domains established above...
-        cd_pll = ClockDomain("pllclk")
-        m.domains += cd_pll
+            # now wire up ResetSignals.  don't mind them being in this domain
+            pll_rst = ResetSignal("pllclk")
+            comb += pll_rst.eq(ResetSignal())
 
         # internal clock is set to selector clock-out.  has the side-effect of
         # running TestIssuer at this speed (see DomainRenamer("intclk") above)
         intclk = ClockSignal("coresync")
         if self.pll_en:
-            comb += intclk.eq(clksel.core_clk_o)
+            comb += intclk.eq(pll.clk_pll_o)
         else:
             comb += intclk.eq(ClockSignal())
 
-        # PLL clock established.  has the side-effect of running clklsel
-        # at the PLL's speed (see DomainRenamer("pllclk") above)
-        pllclk = ClockSignal("pllclk")
-        comb += pllclk.eq(pll.clk_pll_o)
-
-        # wire up external 24mhz to PLL and clksel
-        comb += clksel.clk_24_i.eq(ClockSignal())
-        comb += pll.clk_24_i.eq(clksel.clk_24_i)
-
-        # now wire up ResetSignals.  don't mind them all being in this domain
-        #int_rst = ResetSignal("coresync")
-        pll_rst = ResetSignal("pllclk")
-        #comb += int_rst.eq(ResetSignal())
-        comb += pll_rst.eq(ResetSignal())
-
         return m
 
     def ports(self):
         return list(self.ti.ports()) + list(self.pll.ports()) + \
-               [ClockSignal(), ResetSignal()] + \
-               list(self.clksel.ports())
+               [ClockSignal(), ResetSignal()]
 
     def external_ports(self):
         ports = self.ti.external_ports()
         ports.append(ClockSignal())
         ports.append(ResetSignal())
-        ports.append(self.clksel.clk_sel_i)
-        ports.append(self.clksel.pll_48_o)
+        if self.pll_en:
+            ports.append(self.pll.clk_sel_i)
+            ports.append(self.pll_18_o)
+            ports.append(self.pll.pll_lck_o)
         return ports