add support for DMI debug read of FAST Regfile SPRs
[soc.git] / src / soc / debug / dmi.py
1 """Converted from microwatt core_debug.vhdl to nmigen
2
3 Provides a DMI (Debug Module Interface) for accessing a Libre-SOC core,
4 compatible with microwatt's same interface.
5
6 See constants below for addresses and register formats
7 """
8
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
15
16
17 # DMI register addresses
18 class DBGCore:
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
31
32
33 # CTRL register (direct actions, write 1 to act, read back 0)
34 # bit 0 : Core stop
35 # bit 1 : Core reset (doesn't clear stop)
36 # bit 2 : Icache reset
37 # bit 3 : Single step
38 # bit 4 : Core start
39 class DBGCtrl:
40 STOP = 0
41 RESET = 1
42 ICRESET = 2
43 STEP = 3
44 START = 4
45
46
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)
51 class DBGStat:
52 STOPPING = 0
53 STOPPED = 1
54 TERM = 2
55
56
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
66
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),
74 ]
75
76 class DbgReg(RecordObject):
77 def __init__(self, name):
78 super().__init__(name=name)
79 self.req = Signal()
80 self.ack = Signal()
81 self.addr = Signal(7) # includes fast SPRs, others?
82 self.data = Signal(64)
83
84
85 class DbgCRReg(RecordObject):
86 def __init__(self, name):
87 super().__init__(name=name)
88 self.req = Signal()
89 self.ack = Signal()
90 self.data = Signal(32)
91
92
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")
98
99 # Debug actions
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")
104
105 # Core status inputs
106 self.terminate_i = Signal()
107 self.core_stopped_i = Signal()
108 self.state = CoreState("core_dbg")
109
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
114
115 # Core logging data
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)
120
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)
124
125 # Misc
126 self.terminated_o = Signal()
127
128 def elaborate(self, platform):
129
130 m = Module()
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
133 d_fast = self.d_fast
134
135 # DMI needs fixing... make a one clock pulse
136 dmi_req_i_1 = Signal()
137
138 # Some internal wires
139 stat_reg = Signal(64)
140
141 # Some internal latches
142 stopping = self.stopping_o
143 do_step = Signal()
144 do_reset = Signal()
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)
151 gspr_en = Signal()
152 fast_en = Signal()
153
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()
159
160 LOG_INDEX_BITS = log2_int(self.LOG_LENGTH)
161
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)
177 with m.Default():
178 # everything else is immediate-acknowledgement (combinatorial)
179 comb += dmi.ack_o.eq(dmi.req_i)
180
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
184 terminated)) # bit 2
185
186 # DMI read data mux
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
197 with m.If(gspr_en):
198 comb += dmi.dout.eq(d_gpr.data) # GPR data selected
199 with m.If(fast_en):
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)
211
212 # DMI writes
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)
218
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):
222 with m.If(dmi.we_i):
223 #sync += Display("DMI write to " & to_hstring(dmi_addr))
224
225 # Control register actions
226
227 # Core control
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)
242
243 # GSPR address
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 with m.If(dmi.din == 32): # LR
253 sync += fast_index.eq(FastRegsEnum.LR)
254 sync += fast_en.eq(1)
255 with m.If(dmi.din == 33): # CTR
256 sync += fast_index.eq(FastRegsEnum.CTR)
257 sync += fast_en.eq(1)
258 with m.If(dmi.din == 34): # SRR0
259 sync += fast_index.eq(FastRegsEnum.SRR0)
260 sync += fast_en.eq(1)
261 with m.If(dmi.din == 35): # SRR1
262 sync += fast_index.eq(FastRegsEnum.SRR1)
263 sync += fast_en.eq(1)
264 with m.If(dmi.din == 44): # XER
265 sync += fast_index.eq(FastRegsEnum.XER)
266 sync += fast_en.eq(1)
267 with m.If(dmi.din == 45): # TAR
268 sync += fast_index.eq(FastRegsEnum.XER)
269 sync += fast_en.eq(1)
270
271 # numbering from microwatt:
272 """
273 If(regnum == 32, Display(" LR: %016x", dbg_dout),), # LR
274 If(regnum == 33, Display(" CTR: %016x", dbg_dout),), # CTR
275 If(regnum == 34, Display(" SRR0: %016x", dbg_dout),), # SRR0
276 If(regnum == 35, Display(" SRR1: %016x", dbg_dout),), # SRR1
277 If(regnum == 36, Display(" HSRR0: %016x", dbg_dout),), # HSRR0
278 If(regnum == 37, Display(" HSRR1: %016x", dbg_dout),), # HSRR1
279 If(regnum == 38, Display(" SPRG0: %016x", dbg_dout),), # SPRG0
280 If(regnum == 39, Display(" SPRG1: %016x", dbg_dout),), # SPRG1
281 If(regnum == 40, Display(" SPRG2: %016x", dbg_dout),), # SPRG2
282 If(regnum == 41, Display(" SPRG3: %016x", dbg_dout),), # SPRG3
283 If(regnum == 42, Display(" HSPRG0: %016x", dbg_dout),), # HSPRG0
284 If(regnum == 43, Display(" HSPRG1: %016x", dbg_dout),), # HSPRG1
285 If(regnum == 44, Display(" XER: %016x", dbg_dout),), # XER
286 If(regnum == 45, Display(" TAR: %016x", dbg_dout),), # TAR
287 """
288
289 # Log address
290 with m.Elif(dmi.addr_i == DBGCore.LOG_ADDR):
291 sync += log_dmi_addr.eq(dmi.din)
292 sync += do_dmi_log_rd.eq(1)
293
294 # set PC Halt address
295 with m.Elif(dmi.addr_i == DBGCore.STOPADDR):
296 sync += self.stop_addr_o.eq(dmi.din)
297
298 with m.Else():
299 # sync += Display("DMI read from " & to_string(dmi_addr))
300 pass
301
302 with m.Elif(dmi_read_log_data_1 & ~dmi_read_log_data):
303 # Increment log_dmi_addr after end of read from DBGCore.LOG_DATA
304 lds = log_dmi_addr[:LOG_INDEX_BITS+2]
305 sync += lds.eq(lds + 1)
306 sync += do_dmi_log_rd.eq(1)
307
308 sync += dmi_read_log_data_1.eq(dmi_read_log_data)
309 sync += dmi_read_log_data.eq(dmi.req_i &
310 (dmi.addr_i == DBGCore.LOG_DATA))
311
312 # Set core stop on terminate. We'll be stopping some time *after*
313 # the offending instruction, at least until we can do back flushes
314 # that preserve NIA which we can't just yet.
315 with m.If(self.terminate_i):
316 sync += stopping.eq(1)
317 sync += terminated.eq(1)
318
319 comb += d_gpr.addr.eq(gspr_index)
320 comb += d_fast.addr.eq(fast_index)
321
322 # Core control signals generated by the debug module
323 comb += self.core_stop_o.eq((stopping & ~do_step) | self.terminate_i)
324 comb += self.core_rst_o.eq(do_reset)
325 comb += self.icache_rst_o.eq(do_icreset)
326 comb += self.terminated_o.eq(terminated | self.terminate_i)
327
328 # Logging RAM (none)
329
330 if self.LOG_LENGTH == 0:
331 self.log_read_data_o.eq(0)
332 self.log_write_addr_o.eq(0x00000001)
333
334 return m
335
336 # TODO: debug logging
337 """
338 maybe_log: with m.If(LOG_LENGTH > 0 generate
339 subtype log_ptr_t is unsigned(LOG_INDEX_BITS - 1 downto 0)
340 type log_array_t is array(0 to LOG_LENGTH - 1) of std_ulogic_vector(255 downto 0)
341 signal log_array : log_array_t
342 signal log_rd_ptr : log_ptr_t
343 signal log_wr_ptr : log_ptr_t
344 signal log_toggle = Signal()
345 signal log_wr_enable = Signal()
346 signal log_rd_ptr_latched : log_ptr_t
347 signal log_rd = Signal()_vector(255 downto 0)
348 signal log_dmi_reading = Signal()
349 signal log_dmi_read_done = Signal()
350
351 function select_dword(data = Signal()_vector(255 downto 0)
352 addr = Signal()_vector(31 downto 0)) return std_ulogic_vector is
353 variable firstbit : integer
354 begin
355 firstbit := to_integer(unsigned(addr(1 downto 0))) * 64
356 return data(firstbit + 63 downto firstbit)
357 end
358
359 attribute ram_style : string
360 attribute ram_style of log_array : signal is "block"
361 attribute ram_decomp : string
362 attribute ram_decomp of log_array : signal is "power"
363
364 begin
365 # Use MSB of read addresses to stop the logging
366 log_wr_enable.eq(not (self.log_read_addr(31) or log_dmi_addr(31))
367
368 log_ram: process(clk)
369 begin
370 with m.If(rising_edge(clk)):
371 with m.If(log_wr_enable = '1'):
372 log_array(to_integer(log_wr_ptr)).eq(self.log_data
373 end if
374 log_rd.eq(log_array(to_integer(log_rd_ptr_latched))
375 end if
376 end process
377
378
379 log_buffer: process(clk)
380 variable b : integer
381 variable data = Signal()_vector(255 downto 0)
382 begin
383 with m.If(rising_edge(clk)):
384 with m.If(rst = '1'):
385 log_wr_ptr.eq((others => '0')
386 log_toggle.eq('0'
387 with m.Elif(log_wr_enable = '1'):
388 with m.If(log_wr_ptr = to_unsigned(LOG_LENGTH - 1, LOG_INDEX_BITS)):
389 log_toggle.eq(not log_toggle
390 end if
391 log_wr_ptr.eq(log_wr_ptr + 1
392 end if
393 with m.If(do_dmi_log_rd = '1'):
394 log_rd_ptr_latched.eq(unsigned(log_dmi_addr(LOG_INDEX_BITS + 1 downto 2))
395 else
396 log_rd_ptr_latched.eq(unsigned(self.log_read_addr(LOG_INDEX_BITS + 1 downto 2))
397 end if
398 with m.If(log_dmi_read_done = '1'):
399 log_dmi_data.eq(select_dword(log_rd, log_dmi_addr)
400 else
401 self.log_read_data.eq(select_dword(log_rd, self.log_read_addr)
402 end if
403 log_dmi_read_done.eq(log_dmi_reading
404 log_dmi_reading.eq(do_dmi_log_rd
405 end if
406 end process
407 self.log_write_addr(LOG_INDEX_BITS - 1 downto 0).eq(std_ulogic_vector(log_wr_ptr)
408 self.log_write_addr(LOG_INDEX_BITS).eq('1'
409 self.log_write_addr(31 downto LOG_INDEX_BITS + 1).eq((others => '0')
410 end generate
411
412 """
413
414 def __iter__(self):
415 yield from self.dmi
416 yield self.core_stop_o
417 yield self.core_rst_o
418 yield self.icache_rst_o
419 yield self.terminate_i
420 yield self.core_stopped_i
421 yield from self.state
422 yield from self.d_gpr
423 yield from self.d_cr
424 yield from self.d_xer
425 yield from self.d_fast
426 yield self.log_data_i
427 yield self.log_read_addr_i
428 yield self.log_read_data_o
429 yield self.log_write_addr_o
430 yield self.terminated_o
431
432 def ports(self):
433 return list(self)
434
435
436 def test_debug():
437
438 dut = CoreDebug()
439 vl = rtlil.convert(dut, ports=dut.ports())
440 with open("test_core_debug.il", "w") as f:
441 f.write(vl)
442
443 if __name__ == '__main__':
444 test_debug()
445