1 """Converted from microwatt core_debug.vhdl to nmigen
3 Provides a DMI (Debug Module Interface) for accessing a Libre-SOC core,
4 compatible with microwatt's same interface.
6 See constants below for addresses and register formats
9 from nmigen
import Elaboratable
, Module
, Signal
, Cat
, Const
, Record
, Array
, Mux
10 from nmutil
.iocontrol
import RecordObject
11 from nmigen
.utils
import log2_int
12 from nmigen
.cli
import rtlil
13 from soc
.config
.state
import CoreState
14 from openpower
.consts
import FastRegsEnum
17 # DMI register addresses
19 CTRL
= 0b0000 # Control: start/stop/reset
20 STAT
= 0b0001 # Status (read started/stopped/stopping)
21 NIA
= 0b0010 # NIA register (read only for now)
22 MSR
= 0b0011 # MSR (read only)
23 GSPR_IDX
= 0b0100 # GSPR register index
24 GSPR_DATA
= 0b0101 # GSPR register data
25 LOG_ADDR
= 0b0110 # Log buffer address register
26 LOG_DATA
= 0b0111 # Log buffer data register
27 CR
= 0b1000 # CR (read only)
28 XER
= 0b1001 # XER (read only) - note this is a TEMPORARY hack
29 SVSTATE
= 0b1010 # SVSTATE register (read only for now)
30 STOPADDR
= 0b1011 # Address at which the core automatically stops
33 # CTRL register (direct actions, write 1 to act, read back 0)
35 # bit 1 : Core reset (doesn't clear stop)
36 # bit 2 : Icache reset
47 # STAT register (read only)
48 # bit 0 : Core stopping (wait til bit 1 set)
49 # bit 1 : Core stopped
50 # bit 2 : Core terminated (clears with start or reset)
57 class DMIInterface(RecordObject
):
58 def __init__(self
, name
=None):
59 super().__init
__(name
=name
)
60 self
.addr_i
= Signal(4) # DMI register address
61 self
.din
= Signal(64) # DMI data write in (if we=1)
62 self
.dout
= Signal(64) # DMI data read out (if we=0)
63 self
.req_i
= Signal() # DMI request valid (stb)
64 self
.we_i
= Signal() # DMI write-enable
65 self
.ack_o
= Signal() # DMI ack request
67 def connect_to(self
, other
):
68 return [self
.addr_i
.eq(other
.addr_i
),
69 self
.req_i
.eq(other
.req_i
),
70 self
.we_i
.eq(other
.we_i
),
71 self
.din
.eq(other
.din
),
72 other
.ack_o
.eq(self
.ack_o
),
73 other
.dout
.eq(self
.dout
),
76 class DbgReg(RecordObject
):
77 def __init__(self
, name
):
78 super().__init
__(name
=name
)
81 self
.addr
= Signal(7) # includes fast SPRs, others?
82 self
.data
= Signal(64)
85 class DbgCRReg(RecordObject
):
86 def __init__(self
, name
):
87 super().__init
__(name
=name
)
90 self
.data
= Signal(32)
93 class CoreDebug(Elaboratable
):
94 def __init__(self
, LOG_LENGTH
=0): # TODO - debug log 512):
95 # Length of log buffer
96 self
.LOG_LENGTH
= LOG_LENGTH
97 self
.dmi
= DMIInterface("dmi")
100 self
.core_stop_o
= Signal()
101 self
.core_rst_o
= Signal()
102 self
.icache_rst_o
= Signal()
103 self
.stopping_o
= Signal(name
="stopping")
106 self
.terminate_i
= Signal()
107 self
.core_stopped_i
= Signal()
108 self
.state
= CoreState("core_dbg")
110 self
.d_gpr
= DbgReg("d_gpr") # GSPR register read port
111 self
.d_fast
= DbgReg("d_fast") # GSPR register read port
112 self
.d_cr
= DbgReg("d_cr") # CR register read port
113 self
.d_xer
= DbgReg("d_xer") # XER register read port
116 self
.log_data_i
= Signal(256)
117 self
.log_read_addr_i
= Signal(32)
118 self
.log_read_data_o
= Signal(64)
119 self
.log_write_addr_o
= Signal(32)
121 # address at which the processor stops automatically
122 # set to 0xffffffffffffffff by default (impossible to reach)
123 self
.stop_addr_o
= Signal(64, reset
=-1)
126 self
.terminated_o
= Signal()
128 def elaborate(self
, platform
):
131 comb
, sync
= m
.d
.comb
, m
.d
.sync
132 dmi
, d_gpr
, d_cr
, d_xer
, = self
.dmi
, self
.d_gpr
, self
.d_cr
, self
.d_xer
135 # DMI needs fixing... make a one clock pulse
136 dmi_req_i_1
= Signal()
138 # Some internal wires
139 stat_reg
= Signal(64)
141 # Some internal latches
142 stopping
= self
.stopping_o
145 do_icreset
= Signal()
146 terminated
= Signal()
147 do_gspr_rd
= Signal()
148 # select either GPRs or FAST regs to read, based on GSPR_IDX
149 gspr_index
= Signal
.like(d_gpr
.addr
)
150 fast_index
= Signal
.like(d_gpr
.addr
)
154 log_dmi_addr
= Signal(32)
155 log_dmi_data
= Signal(64)
156 do_dmi_log_rd
= Signal()
157 dmi_read_log_data
= Signal()
158 dmi_read_log_data_1
= Signal()
160 LOG_INDEX_BITS
= log2_int(self
.LOG_LENGTH
)
162 # Single cycle register accesses on DMI except for registers
163 with m
.Switch(dmi
.addr_i
):
164 with m
.Case(DBGCore
.GSPR_DATA
):
165 with m
.If(gspr_en
): # GPR requested, acknowledge GPR
166 comb
+= dmi
.ack_o
.eq(d_gpr
.ack
)
167 comb
+= d_gpr
.req
.eq(dmi
.req_i
)
168 with m
.If(fast_en
): # FAST requested
169 comb
+= dmi
.ack_o
.eq(d_fast
.ack
)
170 comb
+= d_fast
.req
.eq(dmi
.req_i
)
171 with m
.Case(DBGCore
.CR
):
172 comb
+= dmi
.ack_o
.eq(d_cr
.ack
)
173 comb
+= d_cr
.req
.eq(dmi
.req_i
)
174 with m
.Case(DBGCore
.XER
):
175 comb
+= dmi
.ack_o
.eq(d_xer
.ack
)
176 comb
+= d_xer
.req
.eq(dmi
.req_i
)
178 # everything else is immediate-acknowledgement (combinatorial)
179 comb
+= dmi
.ack_o
.eq(dmi
.req_i
)
181 # Status register read composition (DBUG_CORE_STAT_xxx)
182 comb
+= stat_reg
.eq(Cat(stopping
, # bit 0
183 self
.core_stopped_i
, # bit 1
187 with m
.Switch(dmi
.addr_i
):
188 with m
.Case( DBGCore
.STAT
): # Status register
189 comb
+= dmi
.dout
.eq(stat_reg
)
190 with m
.Case( DBGCore
.NIA
): # NIA (PC)
191 comb
+= dmi
.dout
.eq(self
.state
.pc
)
192 with m
.Case( DBGCore
.MSR
): # MSR
193 comb
+= dmi
.dout
.eq(self
.state
.msr
)
194 with m
.Case( DBGCore
.SVSTATE
): # SVSTATE
195 comb
+= dmi
.dout
.eq(self
.state
.svstate
)
196 with m
.Case( DBGCore
.GSPR_DATA
): # GPR/FAST regs
198 comb
+= dmi
.dout
.eq(d_gpr
.data
) # GPR data selected
200 comb
+= dmi
.dout
.eq(d_fast
.data
) # FAST reg read selected
201 with m
.Case( DBGCore
.LOG_ADDR
): # Logging
202 comb
+= dmi
.dout
.eq(Cat(log_dmi_addr
, self
.log_write_addr_o
))
203 with m
.Case( DBGCore
.LOG_DATA
):
204 comb
+= dmi
.dout
.eq(log_dmi_data
)
205 with m
.Case(DBGCore
.CR
): # CR
206 comb
+= dmi
.dout
.eq(d_cr
.data
)
207 with m
.Case(DBGCore
.XER
): # XER
208 comb
+= dmi
.dout
.eq(d_xer
.data
)
209 with m
.Case(DBGCore
.STOPADDR
): # Halt PC
210 comb
+= dmi
.dout
.eq(self
.stop_addr_o
)
213 # Reset the 1-cycle "do" signals
214 sync
+= do_step
.eq(0)
215 sync
+= do_reset
.eq(0)
216 sync
+= do_icreset
.eq(0)
217 sync
+= do_dmi_log_rd
.eq(0)
219 # Edge detect on dmi_req_i for 1-shot pulses
220 sync
+= dmi_req_i_1
.eq(dmi
.req_i
)
221 with m
.If(dmi
.req_i
& ~dmi_req_i_1
):
223 #sync += Display("DMI write to " & to_hstring(dmi_addr))
225 # Control register actions
228 with m
.If(dmi
.addr_i
== DBGCore
.CTRL
):
229 with m
.If(dmi
.din
[DBGCtrl
.RESET
]):
230 sync
+= do_reset
.eq(1)
231 sync
+= terminated
.eq(0)
232 with m
.If(dmi
.din
[DBGCtrl
.STOP
]):
233 sync
+= stopping
.eq(1)
234 with m
.If(dmi
.din
[DBGCtrl
.STEP
]):
235 sync
+= do_step
.eq(1)
236 sync
+= terminated
.eq(0)
237 with m
.If(dmi
.din
[DBGCtrl
.ICRESET
]):
238 sync
+= do_icreset
.eq(1)
239 with m
.If(dmi
.din
[DBGCtrl
.START
]):
240 sync
+= stopping
.eq(0)
241 sync
+= terminated
.eq(0)
244 with m
.Elif(dmi
.addr_i
== DBGCore
.GSPR_IDX
):
245 sync
+= gspr_index
.eq(0)
246 sync
+= fast_index
.eq(0)
247 sync
+= gspr_en
.eq(0)
248 sync
+= fast_en
.eq(0)
249 with m
.If(dmi
.din
<= 31):
250 sync
+= gspr_index
.eq(dmi
.din
)
251 sync
+= gspr_en
.eq(1)
252 # cover the FastRegs LR, CTR, SRR0, SRR1 etc.
253 # numbering is from microwatt
254 for x
, i
in FastRegsEnum
.__dict
__.items():
255 if not isinstance(i
, int) or x
== 'N_REGS':
257 with m
.If(dmi
.din
== 32+i
):
258 sync
+= fast_index
.eq(i
)
259 sync
+= fast_en
.eq(1)
262 with m
.Elif(dmi
.addr_i
== DBGCore
.LOG_ADDR
):
263 sync
+= log_dmi_addr
.eq(dmi
.din
)
264 sync
+= do_dmi_log_rd
.eq(1)
266 # set PC Halt address
267 with m
.Elif(dmi
.addr_i
== DBGCore
.STOPADDR
):
268 sync
+= self
.stop_addr_o
.eq(dmi
.din
)
271 # sync += Display("DMI read from " & to_string(dmi_addr))
274 with m
.Elif(dmi_read_log_data_1
& ~dmi_read_log_data
):
275 # Increment log_dmi_addr after end of read from DBGCore.LOG_DATA
276 lds
= log_dmi_addr
[:LOG_INDEX_BITS
+2]
277 sync
+= lds
.eq(lds
+ 1)
278 sync
+= do_dmi_log_rd
.eq(1)
280 sync
+= dmi_read_log_data_1
.eq(dmi_read_log_data
)
281 sync
+= dmi_read_log_data
.eq(dmi
.req_i
&
282 (dmi
.addr_i
== DBGCore
.LOG_DATA
))
284 # Set core stop on terminate. We'll be stopping some time *after*
285 # the offending instruction, at least until we can do back flushes
286 # that preserve NIA which we can't just yet.
287 with m
.If(self
.terminate_i
):
288 sync
+= stopping
.eq(1)
289 sync
+= terminated
.eq(1)
291 comb
+= d_gpr
.addr
.eq(gspr_index
)
292 comb
+= d_fast
.addr
.eq(fast_index
)
294 # Core control signals generated by the debug module
295 # Note: make stop and terminated synchronous, to help with timing
296 # however this *may* interfere with some of the DMI-based unit tests
297 # so has to be kept an eye on
298 sync
+= self
.core_stop_o
.eq((stopping
& ~do_step
) | self
.terminate_i
)
299 sync
+= self
.terminated_o
.eq(terminated | self
.terminate_i
)
300 comb
+= self
.core_rst_o
.eq(do_reset
)
301 comb
+= self
.icache_rst_o
.eq(do_icreset
)
305 if self
.LOG_LENGTH
== 0:
306 self
.log_read_data_o
.eq(0)
307 self
.log_write_addr_o
.eq(0x00000001)
311 # TODO: debug logging
313 maybe_log: with m.If(LOG_LENGTH > 0 generate
314 subtype log_ptr_t is unsigned(LOG_INDEX_BITS - 1 downto 0)
315 type log_array_t is array(0 to LOG_LENGTH - 1) of std_ulogic_vector(255 downto 0)
316 signal log_array : log_array_t
317 signal log_rd_ptr : log_ptr_t
318 signal log_wr_ptr : log_ptr_t
319 signal log_toggle = Signal()
320 signal log_wr_enable = Signal()
321 signal log_rd_ptr_latched : log_ptr_t
322 signal log_rd = Signal()_vector(255 downto 0)
323 signal log_dmi_reading = Signal()
324 signal log_dmi_read_done = Signal()
326 function select_dword(data = Signal()_vector(255 downto 0)
327 addr = Signal()_vector(31 downto 0)) return std_ulogic_vector is
328 variable firstbit : integer
330 firstbit := to_integer(unsigned(addr(1 downto 0))) * 64
331 return data(firstbit + 63 downto firstbit)
334 attribute ram_style : string
335 attribute ram_style of log_array : signal is "block"
336 attribute ram_decomp : string
337 attribute ram_decomp of log_array : signal is "power"
340 # Use MSB of read addresses to stop the logging
341 log_wr_enable.eq(not (self.log_read_addr(31) or log_dmi_addr(31))
343 log_ram: process(clk)
345 with m.If(rising_edge(clk)):
346 with m.If(log_wr_enable = '1'):
347 log_array(to_integer(log_wr_ptr)).eq(self.log_data
349 log_rd.eq(log_array(to_integer(log_rd_ptr_latched))
354 log_buffer: process(clk)
356 variable data = Signal()_vector(255 downto 0)
358 with m.If(rising_edge(clk)):
359 with m.If(rst = '1'):
360 log_wr_ptr.eq((others => '0')
362 with m.Elif(log_wr_enable = '1'):
363 with m.If(log_wr_ptr = to_unsigned(LOG_LENGTH - 1, LOG_INDEX_BITS)):
364 log_toggle.eq(not log_toggle
366 log_wr_ptr.eq(log_wr_ptr + 1
368 with m.If(do_dmi_log_rd = '1'):
369 log_rd_ptr_latched.eq(unsigned(log_dmi_addr(LOG_INDEX_BITS + 1 downto 2))
371 log_rd_ptr_latched.eq(unsigned(self.log_read_addr(LOG_INDEX_BITS + 1 downto 2))
373 with m.If(log_dmi_read_done = '1'):
374 log_dmi_data.eq(select_dword(log_rd, log_dmi_addr)
376 self.log_read_data.eq(select_dword(log_rd, self.log_read_addr)
378 log_dmi_read_done.eq(log_dmi_reading
379 log_dmi_reading.eq(do_dmi_log_rd
382 self.log_write_addr(LOG_INDEX_BITS - 1 downto 0).eq(std_ulogic_vector(log_wr_ptr)
383 self.log_write_addr(LOG_INDEX_BITS).eq('1'
384 self.log_write_addr(31 downto LOG_INDEX_BITS + 1).eq((others => '0')
391 yield self
.core_stop_o
392 yield self
.core_rst_o
393 yield self
.icache_rst_o
394 yield self
.terminate_i
395 yield self
.core_stopped_i
396 yield from self
.state
397 yield from self
.d_gpr
399 yield from self
.d_xer
400 yield from self
.d_fast
401 yield self
.log_data_i
402 yield self
.log_read_addr_i
403 yield self
.log_read_data_o
404 yield self
.log_write_addr_o
405 yield self
.terminated_o
414 vl
= rtlil
.convert(dut
, ports
=dut
.ports())
415 with
open("test_core_debug.il", "w") as f
:
418 if __name__
== '__main__':