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
+ 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
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)
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):
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.state = CoreState("core_dbg")
- # GSPR register read port
- self.dbg_gpr = DbgReg("dbg_gpr")
-
- # CR register read port
- self.dbg_cr = DbgReg("dbg_cr")
+ 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)
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()
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()
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)
+ # 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)
LOG_INDEX_BITS = log2_int(self.LOG_LENGTH)
- # Single cycle register accesses on DMI except for GSPR data
- with m.Switch(self.dmi.addr_i):
+ # Single cycle register accesses on DMI except for registers
+ with m.Switch(dmi.addr_i):
with m.Case(DBGCore.GSPR_DATA):
- comb += self.dmi.ack_o.eq(self.dbg_gpr.ack)
- comb += self.dbg_gpr.req.eq(self.dmi.req_i)
+ 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 += self.dmi.ack_o.eq(self.dbg_cr.ack)
- comb += self.dbg_cr.req.eq(self.dmi.req_i)
+ 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():
- comb += self.dmi.ack_o.eq(self.dmi.req_i)
+ # 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
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.state.pc)
- with m.Case( DBGCore.MSR):
- comb += self.dmi.dout.eq(self.state.msr)
- with m.Case( DBGCore.GSPR_DATA):
- comb += self.dmi.dout.eq(self.dbg_gpr.data)
- 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)
- with m.Case(DBGCore.CR):
- comb += self.dmi.dout.eq(self.dbg_cr.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
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_IDX):
- 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
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
sync += stopping.eq(1)
sync += terminated.eq(1)
- comb += self.dbg_gpr.addr.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)
yield self.terminate_i
yield self.core_stopped_i
yield from self.state
- yield from self.dbg_gpr
+ 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