name core_stop and terminated_o synchronous to potentially help
[soc.git] / src / soc / debug / dmi.py
index 2a72786f44f19ce97b70890f0e34a531e4db7b90..03bd8dc8eabcde75a191147a666ad9086b120ac5 100644 (file)
@@ -10,18 +10,24 @@ from nmigen import Elaboratable, Module, Signal, Cat, Const, Record, Array, Mux
 from nmutil.iocontrol import RecordObject
 from nmigen.utils import log2_int
 from nmigen.cli import rtlil
+from soc.config.state import CoreState
+from openpower.consts import FastRegsEnum
 
 
 # DMI register addresses
 class DBGCore:
-    CTRL           = 0b0000
-    STAT           = 0b0001
-    NIA            = 0b0010 # NIA register (read only for now)
-    MSR            = 0b0011 # MSR (read only)
-    GSPR_INDEX     = 0b0100 # GSPR register index
-    GSPR_DATA      = 0b0101 # GSPR register data
-    LOG_ADDR       = 0b0110 # Log buffer address register
-    LOG_DATA       = 0b0111 # Log buffer data register
+    CTRL         = 0b0000 # Control: start/stop/reset
+    STAT         = 0b0001 # Status (read started/stopped/stopping)
+    NIA          = 0b0010 # NIA register (read only for now)
+    MSR          = 0b0011 # MSR (read only)
+    GSPR_IDX     = 0b0100 # GSPR register index
+    GSPR_DATA    = 0b0101 # GSPR register data
+    LOG_ADDR     = 0b0110 # Log buffer address register
+    LOG_DATA     = 0b0111 # Log buffer data register
+    CR           = 0b1000 # CR (read only)
+    XER          = 0b1001 # XER (read only) - note this is a TEMPORARY hack
+    SVSTATE      = 0b1010 # SVSTATE register (read only for now)
+    STOPADDR     = 0b1011 # Address at which the core automatically stops
 
 
 # CTRL register (direct actions, write 1 to act, read back 0)
@@ -49,23 +55,39 @@ class DBGStat:
 
 
 class DMIInterface(RecordObject):
-    def __init__(self, name):
+    def __init__(self, name=None):
         super().__init__(name=name)
         self.addr_i = Signal(4)   # DMI register address
-        self.din    = Signal(64)  # DMI data in (if we=1)
-        self.dout   = Signal(64)  # DMI data out (if we=0)
+        self.din    = Signal(64)  # DMI data write in (if we=1)
+        self.dout   = Signal(64)  # DMI data read out (if we=0)
         self.req_i  = Signal()    # DMI request valid (stb)
         self.we_i   = Signal()    # DMI write-enable
         self.ack_o  = Signal()    # DMI ack request
 
+    def connect_to(self, other):
+        return [self.addr_i.eq(other.addr_i),
+                self.req_i.eq(other.req_i),
+                self.we_i.eq(other.we_i),
+                self.din.eq(other.din),
+                other.ack_o.eq(self.ack_o),
+                other.dout.eq(self.dout),
+                ]
 
 class DbgReg(RecordObject):
     def __init__(self, name):
         super().__init__(name=name)
-        self.req_o     = Signal()
-        self.ack_i     = Signal()
-        self.addr_o    = Signal(7) #  includes fast SPRs, others?
-        self.data_i    = Signal(64)
+        self.req     = Signal()
+        self.ack     = Signal()
+        self.addr    = Signal(7) #  includes fast SPRs, others?
+        self.data    = Signal(64)
+
+
+class DbgCRReg(RecordObject):
+    def __init__(self, name):
+        super().__init__(name=name)
+        self.req     = Signal()
+        self.ack     = Signal()
+        self.data    = Signal(32)
 
 
 class CoreDebug(Elaboratable):
@@ -78,15 +100,17 @@ class CoreDebug(Elaboratable):
         self.core_stop_o       = Signal()
         self.core_rst_o        = Signal()
         self.icache_rst_o      = Signal()
+        self.stopping_o = Signal(name="stopping")
 
         # Core status inputs
         self.terminate_i    = Signal()
         self.core_stopped_i = Signal()
-        self.nia            = Signal(64)
-        self.msr            = Signal(64)
+        self.state = CoreState("core_dbg")
 
-        # GSPR register read port
-        self.dbg_gpr = DbgReg("dbg_gpr")
+        self.d_gpr = DbgReg("d_gpr") # GSPR register read port
+        self.d_fast = DbgReg("d_fast") # GSPR register read port
+        self.d_cr = DbgReg("d_cr")   # CR register read port
+        self.d_xer = DbgReg("d_xer") # XER register read port
 
         # Core logging data
         self.log_data_i        = Signal(256)
@@ -94,6 +118,10 @@ class CoreDebug(Elaboratable):
         self.log_read_data_o   = Signal(64)
         self.log_write_addr_o  = Signal(32)
 
+        # address at which the processor stops automatically
+        # set to 0xffffffffffffffff by default (impossible to reach)
+        self.stop_addr_o = Signal(64, reset=-1)
+
         # Misc
         self.terminated_o  = Signal()
 
@@ -101,6 +129,8 @@ class CoreDebug(Elaboratable):
 
         m = Module()
         comb, sync = m.d.comb, m.d.sync
+        dmi, d_gpr, d_cr, d_xer, = self.dmi, self.d_gpr, self.d_cr, self.d_xer
+        d_fast = self.d_fast
 
         # DMI needs fixing... make a one clock pulse
         dmi_req_i_1 = Signal()
@@ -109,13 +139,17 @@ class CoreDebug(Elaboratable):
         stat_reg = Signal(64)
 
         # Some internal latches
-        stopping     = Signal()
+        stopping     = self.stopping_o
         do_step      = Signal()
         do_reset     = Signal()
         do_icreset   = Signal()
         terminated   = Signal()
         do_gspr_rd   = Signal()
-        gspr_index   = Signal.like(self.dbg_gpr.addr_o)
+        # select either GPRs or FAST regs to read, based on GSPR_IDX
+        gspr_index   = Signal.like(d_gpr.addr)
+        fast_index   = Signal.like(d_gpr.addr)
+        gspr_en      = Signal()
+        fast_en      = Signal()
 
         log_dmi_addr = Signal(32)
         log_dmi_data = Signal(64)
@@ -125,11 +159,24 @@ class CoreDebug(Elaboratable):
 
         LOG_INDEX_BITS = log2_int(self.LOG_LENGTH)
 
-        # Single cycle register accesses on DMI except for GSPR data
-        comb += self.dmi.ack_o.eq(Mux(self.dmi.addr_i == DBGCore.GSPR_DATA,
-                                      self.dbg_gpr.ack_i, self.dmi.req_i))
-        comb += self.dbg_gpr.req_o.eq(Mux(self.dmi.addr_i == DBGCore.GSPR_DATA,
-                                      self.dmi.req_i, 0))
+        # Single cycle register accesses on DMI except for registers
+        with m.Switch(dmi.addr_i):
+            with m.Case(DBGCore.GSPR_DATA):
+                with m.If(gspr_en): # GPR requested, acknowledge GPR
+                    comb += dmi.ack_o.eq(d_gpr.ack)
+                    comb += d_gpr.req.eq(dmi.req_i)
+                with m.If(fast_en): # FAST requested
+                    comb += dmi.ack_o.eq(d_fast.ack)
+                    comb += d_fast.req.eq(dmi.req_i)
+            with m.Case(DBGCore.CR):
+                comb += dmi.ack_o.eq(d_cr.ack)
+                comb += d_cr.req.eq(dmi.req_i)
+            with m.Case(DBGCore.XER):
+                comb += dmi.ack_o.eq(d_xer.ack)
+                comb += d_xer.req.eq(dmi.req_i)
+            with m.Default():
+                # everything else is immediate-acknowledgement (combinatorial)
+                comb += dmi.ack_o.eq(dmi.req_i)
 
         # Status register read composition (DBUG_CORE_STAT_xxx)
         comb += stat_reg.eq(Cat(stopping,            # bit 0
@@ -137,20 +184,30 @@ class CoreDebug(Elaboratable):
                                 terminated))         # bit 2
 
         # DMI read data mux
-        with m.Switch(self.dmi.addr_i):
-            with m.Case( DBGCore.STAT):
-                comb += self.dmi.dout.eq(stat_reg)
-            with m.Case( DBGCore.NIA):
-                comb += self.dmi.dout.eq(self.nia)
-            with m.Case( DBGCore.MSR):
-                comb += self.dmi.dout.eq(self.msr)
-            with m.Case( DBGCore.GSPR_DATA):
-                comb += self.dmi.dout.eq(self.dbg_gpr.data_i)
-            with m.Case( DBGCore.LOG_ADDR):
-                comb += self.dmi.dout.eq(Cat(log_dmi_addr,
-                                             self.log_write_addr_o))
+        with m.Switch(dmi.addr_i):
+            with m.Case( DBGCore.STAT):               # Status register
+                comb += dmi.dout.eq(stat_reg)
+            with m.Case( DBGCore.NIA):                # NIA (PC)
+                comb += dmi.dout.eq(self.state.pc)
+            with m.Case( DBGCore.MSR):                # MSR
+                comb += dmi.dout.eq(self.state.msr)
+            with m.Case( DBGCore.SVSTATE):            # SVSTATE
+                comb += dmi.dout.eq(self.state.svstate)
+            with m.Case( DBGCore.GSPR_DATA):          # GPR/FAST regs
+                with m.If(gspr_en):
+                    comb += dmi.dout.eq(d_gpr.data)   # GPR data selected
+                with m.If(fast_en):
+                    comb += dmi.dout.eq(d_fast.data)  # FAST reg read selected
+            with m.Case( DBGCore.LOG_ADDR):           # Logging
+                comb += dmi.dout.eq(Cat(log_dmi_addr, self.log_write_addr_o))
             with m.Case( DBGCore.LOG_DATA):
-                comb += self.dmi.dout.eq(log_dmi_data)
+                comb += dmi.dout.eq(log_dmi_data)
+            with m.Case(DBGCore.CR):                  # CR
+                comb += dmi.dout.eq(d_cr.data)
+            with m.Case(DBGCore.XER):                 # XER
+                comb += dmi.dout.eq(d_xer.data)
+            with m.Case(DBGCore.STOPADDR):            # Halt PC
+                comb += dmi.dout.eq(self.stop_addr_o)
 
         # DMI writes
         # Reset the 1-cycle "do" signals
@@ -160,37 +217,56 @@ class CoreDebug(Elaboratable):
         sync += do_dmi_log_rd.eq(0)
 
         # Edge detect on dmi_req_i for 1-shot pulses
-        sync += dmi_req_i_1.eq(self.dmi.req_i)
-        with m.If(self.dmi.req_i & ~dmi_req_i_1):
-            with m.If(self.dmi.we_i):
+        sync += dmi_req_i_1.eq(dmi.req_i)
+        with m.If(dmi.req_i & ~dmi_req_i_1):
+            with m.If(dmi.we_i):
                 #sync += Display("DMI write to " & to_hstring(dmi_addr))
 
                 # Control register actions
 
                 # Core control
-                with m.If(self.dmi.addr_i == DBGCore.CTRL):
-                    with m.If(self.dmi.din[DBGCtrl.RESET]):
+                with m.If(dmi.addr_i == DBGCore.CTRL):
+                    with m.If(dmi.din[DBGCtrl.RESET]):
                         sync += do_reset.eq(1)
                         sync += terminated.eq(0)
-                    with m.If(self.dmi.din[DBGCtrl.STOP]):
+                    with m.If(dmi.din[DBGCtrl.STOP]):
                         sync += stopping.eq(1)
-                    with m.If(self.dmi.din[DBGCtrl.STEP]):
+                    with m.If(dmi.din[DBGCtrl.STEP]):
                         sync += do_step.eq(1)
                         sync += terminated.eq(0)
-                    with m.If(self.dmi.din[DBGCtrl.ICRESET]):
+                    with m.If(dmi.din[DBGCtrl.ICRESET]):
                         sync += do_icreset.eq(1)
-                    with m.If(self.dmi.din[DBGCtrl.START]):
+                    with m.If(dmi.din[DBGCtrl.START]):
                         sync += stopping.eq(0)
                         sync += terminated.eq(0)
 
                 # GSPR address
-                with m.Elif(self.dmi.addr_i == DBGCore.GSPR_INDEX):
-                    sync += gspr_index.eq(self.dmi.din)
+                with m.Elif(dmi.addr_i == DBGCore.GSPR_IDX):
+                    sync += gspr_index.eq(0)
+                    sync += fast_index.eq(0)
+                    sync += gspr_en.eq(0)
+                    sync += fast_en.eq(0)
+                    with m.If(dmi.din <= 31):
+                        sync += gspr_index.eq(dmi.din)
+                        sync += gspr_en.eq(1)
+                    # cover the FastRegs LR, CTR, SRR0, SRR1 etc.
+                    # numbering is from microwatt
+                    for x, i in FastRegsEnum.__dict__.items():
+                        if not isinstance(i, int) or x == 'N_REGS':
+                            continue
+                        with m.If(dmi.din == 32+i):
+                            sync += fast_index.eq(i)
+                            sync += fast_en.eq(1)
 
                 # Log address
-                with m.Elif(self.dmi.addr_i == DBGCore.LOG_ADDR):
-                    sync += log_dmi_addr.eq(self.dmi.din)
+                with m.Elif(dmi.addr_i == DBGCore.LOG_ADDR):
+                    sync += log_dmi_addr.eq(dmi.din)
                     sync += do_dmi_log_rd.eq(1)
+
+                # set PC Halt address
+                with m.Elif(dmi.addr_i == DBGCore.STOPADDR):
+                    sync += self.stop_addr_o.eq(dmi.din)
+
             with m.Else():
                 # sync += Display("DMI read from " & to_string(dmi_addr))
                 pass
@@ -202,8 +278,8 @@ class CoreDebug(Elaboratable):
             sync += do_dmi_log_rd.eq(1)
 
         sync += dmi_read_log_data_1.eq(dmi_read_log_data)
-        sync += dmi_read_log_data.eq(self.dmi.req_i &
-                                     (self.dmi.addr_i == DBGCore.LOG_DATA))
+        sync += dmi_read_log_data.eq(dmi.req_i &
+                                     (dmi.addr_i == DBGCore.LOG_DATA))
 
         # Set core stop on terminate. We'll be stopping some time *after*
         # the offending instruction, at least until we can do back flushes
@@ -212,13 +288,17 @@ class CoreDebug(Elaboratable):
             sync += stopping.eq(1)
             sync += terminated.eq(1)
 
-        comb += self.dbg_gpr.addr_o.eq(gspr_index)
+        comb += d_gpr.addr.eq(gspr_index)
+        comb += d_fast.addr.eq(fast_index)
 
         # Core control signals generated by the debug module
-        comb += self.core_stop_o.eq(stopping & ~do_step)
+        # Note: make stop and terminated synchronous, to help with timing
+        # however this *may* interfere with some of the DMI-based unit tests
+        # so has to be kept an eye on
+        sync += self.core_stop_o.eq((stopping & ~do_step) | self.terminate_i)
+        sync += self.terminated_o.eq(terminated | self.terminate_i)
         comb += self.core_rst_o.eq(do_reset)
         comb += self.icache_rst_o.eq(do_icreset)
-        comb += self.terminated_o.eq(terminated)
 
         # Logging RAM (none)
 
@@ -313,9 +393,11 @@ class CoreDebug(Elaboratable):
         yield self.icache_rst_o
         yield self.terminate_i
         yield self.core_stopped_i
-        yield self.nia
-        yield self.msr
-        yield from self.dbg_gpr
+        yield from self.state
+        yield from self.d_gpr
+        yield from self.d_cr
+        yield from self.d_xer
+        yield from self.d_fast
         yield self.log_data_i
         yield self.log_read_addr_i
         yield self.log_read_data_o