add DMI STOPADDR register and use it in HDLRunner to halt simulations
[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
15
16 # DMI register addresses
17 class DBGCore:
18 CTRL = 0b0000
19 STAT = 0b0001
20 NIA = 0b0010 # NIA register (read only for now)
21 MSR = 0b0011 # MSR (read only)
22 GSPR_IDX = 0b0100 # GSPR register index
23 GSPR_DATA = 0b0101 # GSPR register data
24 LOG_ADDR = 0b0110 # Log buffer address register
25 LOG_DATA = 0b0111 # Log buffer data register
26 CR = 0b1000 # CR (read only)
27 XER = 0b1001 # XER (read only) - note this is a TEMPORARY hack
28 SVSTATE = 0b1010 # SVSTATE register (read only for now)
29 STOPADDR = 0b1011 # Address at which the core automatically stops
30
31
32 # CTRL register (direct actions, write 1 to act, read back 0)
33 # bit 0 : Core stop
34 # bit 1 : Core reset (doesn't clear stop)
35 # bit 2 : Icache reset
36 # bit 3 : Single step
37 # bit 4 : Core start
38 class DBGCtrl:
39 STOP = 0
40 RESET = 1
41 ICRESET = 2
42 STEP = 3
43 START = 4
44
45
46 # STAT register (read only)
47 # bit 0 : Core stopping (wait til bit 1 set)
48 # bit 1 : Core stopped
49 # bit 2 : Core terminated (clears with start or reset)
50 class DBGStat:
51 STOPPING = 0
52 STOPPED = 1
53 TERM = 2
54
55
56 class DMIInterface(RecordObject):
57 def __init__(self, name=None):
58 super().__init__(name=name)
59 self.addr_i = Signal(4) # DMI register address
60 self.din = Signal(64) # DMI data write in (if we=1)
61 self.dout = Signal(64) # DMI data read out (if we=0)
62 self.req_i = Signal() # DMI request valid (stb)
63 self.we_i = Signal() # DMI write-enable
64 self.ack_o = Signal() # DMI ack request
65
66 def connect_to(self, other):
67 return [self.addr_i.eq(other.addr_i),
68 self.req_i.eq(other.req_i),
69 self.we_i.eq(other.we_i),
70 self.din.eq(other.din),
71 other.ack_o.eq(self.ack_o),
72 other.dout.eq(self.dout),
73 ]
74
75 class DbgReg(RecordObject):
76 def __init__(self, name):
77 super().__init__(name=name)
78 self.req = Signal()
79 self.ack = Signal()
80 self.addr = Signal(7) # includes fast SPRs, others?
81 self.data = Signal(64)
82
83
84 class DbgCRReg(RecordObject):
85 def __init__(self, name):
86 super().__init__(name=name)
87 self.req = Signal()
88 self.ack = Signal()
89 self.data = Signal(32)
90
91
92 class CoreDebug(Elaboratable):
93 def __init__(self, LOG_LENGTH=0): # TODO - debug log 512):
94 # Length of log buffer
95 self.LOG_LENGTH = LOG_LENGTH
96 self.dmi = DMIInterface("dmi")
97
98 # Debug actions
99 self.core_stop_o = Signal()
100 self.core_rst_o = Signal()
101 self.icache_rst_o = Signal()
102 self.stopping_o = Signal(name="stopping")
103
104 # Core status inputs
105 self.terminate_i = Signal()
106 self.core_stopped_i = Signal()
107 self.state = CoreState("core_dbg")
108
109 # GSPR register read port
110 self.d_gpr = DbgReg("d_gpr")
111
112 # CR register read port
113 self.d_cr = DbgReg("d_cr")
114
115 # XER register read port
116 self.d_xer = DbgReg("d_xer")
117
118 # Core logging data
119 self.log_data_i = Signal(256)
120 self.log_read_addr_i = Signal(32)
121 self.log_read_data_o = Signal(64)
122 self.log_write_addr_o = Signal(32)
123
124 # address at which the processor stops automatically
125 # set to 0xffffffffffffffff by default (impossible to reach)
126 self.stop_addr_o = Signal(64, reset=-1)
127
128 # Misc
129 self.terminated_o = Signal()
130
131 def elaborate(self, platform):
132
133 m = Module()
134 comb, sync = m.d.comb, m.d.sync
135 dmi, d_gpr, d_cr, d_xer, = self.dmi, self.d_gpr, self.d_cr, self.d_xer
136
137 # DMI needs fixing... make a one clock pulse
138 dmi_req_i_1 = Signal()
139
140 # Some internal wires
141 stat_reg = Signal(64)
142
143 # Some internal latches
144 stopping = self.stopping_o
145 do_step = Signal()
146 do_reset = Signal()
147 do_icreset = Signal()
148 terminated = Signal()
149 do_gspr_rd = Signal()
150 gspr_index = Signal.like(d_gpr.addr)
151
152 log_dmi_addr = Signal(32)
153 log_dmi_data = Signal(64)
154 do_dmi_log_rd = Signal()
155 dmi_read_log_data = Signal()
156 dmi_read_log_data_1 = Signal()
157
158 LOG_INDEX_BITS = log2_int(self.LOG_LENGTH)
159
160 # Single cycle register accesses on DMI except for GSPR data
161 with m.Switch(dmi.addr_i):
162 with m.Case(DBGCore.GSPR_DATA):
163 comb += dmi.ack_o.eq(d_gpr.ack)
164 comb += d_gpr.req.eq(dmi.req_i)
165 with m.Case(DBGCore.CR):
166 comb += dmi.ack_o.eq(d_cr.ack)
167 comb += d_cr.req.eq(dmi.req_i)
168 with m.Case(DBGCore.XER):
169 comb += dmi.ack_o.eq(d_xer.ack)
170 comb += d_xer.req.eq(dmi.req_i)
171 with m.Default():
172 comb += dmi.ack_o.eq(dmi.req_i)
173
174 # Status register read composition (DBUG_CORE_STAT_xxx)
175 comb += stat_reg.eq(Cat(stopping, # bit 0
176 self.core_stopped_i, # bit 1
177 terminated)) # bit 2
178
179 # DMI read data mux
180 with m.Switch(dmi.addr_i):
181 with m.Case( DBGCore.STAT):
182 comb += dmi.dout.eq(stat_reg)
183 with m.Case( DBGCore.NIA):
184 comb += dmi.dout.eq(self.state.pc)
185 with m.Case( DBGCore.MSR):
186 comb += dmi.dout.eq(self.state.msr)
187 with m.Case( DBGCore.SVSTATE):
188 comb += dmi.dout.eq(self.state.svstate)
189 with m.Case( DBGCore.GSPR_DATA):
190 comb += dmi.dout.eq(d_gpr.data)
191 with m.Case( DBGCore.LOG_ADDR):
192 comb += dmi.dout.eq(Cat(log_dmi_addr, self.log_write_addr_o))
193 with m.Case( DBGCore.LOG_DATA):
194 comb += dmi.dout.eq(log_dmi_data)
195 with m.Case(DBGCore.CR):
196 comb += dmi.dout.eq(d_cr.data)
197 with m.Case(DBGCore.XER):
198 comb += dmi.dout.eq(d_xer.data)
199 with m.Case(DBGCore.STOPADDR): # Halt PC
200 comb += dmi.dout.eq(self.stop_addr_o)
201
202 # DMI writes
203 # Reset the 1-cycle "do" signals
204 sync += do_step.eq(0)
205 sync += do_reset.eq(0)
206 sync += do_icreset.eq(0)
207 sync += do_dmi_log_rd.eq(0)
208
209 # Edge detect on dmi_req_i for 1-shot pulses
210 sync += dmi_req_i_1.eq(dmi.req_i)
211 with m.If(dmi.req_i & ~dmi_req_i_1):
212 with m.If(dmi.we_i):
213 #sync += Display("DMI write to " & to_hstring(dmi_addr))
214
215 # Control register actions
216
217 # Core control
218 with m.If(dmi.addr_i == DBGCore.CTRL):
219 with m.If(dmi.din[DBGCtrl.RESET]):
220 sync += do_reset.eq(1)
221 sync += terminated.eq(0)
222 with m.If(dmi.din[DBGCtrl.STOP]):
223 sync += stopping.eq(1)
224 with m.If(dmi.din[DBGCtrl.STEP]):
225 sync += do_step.eq(1)
226 sync += terminated.eq(0)
227 with m.If(dmi.din[DBGCtrl.ICRESET]):
228 sync += do_icreset.eq(1)
229 with m.If(dmi.din[DBGCtrl.START]):
230 sync += stopping.eq(0)
231 sync += terminated.eq(0)
232
233 # GSPR address
234 with m.Elif(dmi.addr_i == DBGCore.GSPR_IDX):
235 sync += gspr_index.eq(dmi.din)
236
237 # Log address
238 with m.Elif(dmi.addr_i == DBGCore.LOG_ADDR):
239 sync += log_dmi_addr.eq(dmi.din)
240 sync += do_dmi_log_rd.eq(1)
241
242 # set PC Halt address
243 with m.Elif(dmi.addr_i == DBGCore.STOPADDR):
244 sync += self.stop_addr_o.eq(dmi.din)
245
246 with m.Else():
247 # sync += Display("DMI read from " & to_string(dmi_addr))
248 pass
249
250 with m.Elif(dmi_read_log_data_1 & ~dmi_read_log_data):
251 # Increment log_dmi_addr after end of read from DBGCore.LOG_DATA
252 lds = log_dmi_addr[:LOG_INDEX_BITS+2]
253 sync += lds.eq(lds + 1)
254 sync += do_dmi_log_rd.eq(1)
255
256 sync += dmi_read_log_data_1.eq(dmi_read_log_data)
257 sync += dmi_read_log_data.eq(dmi.req_i &
258 (dmi.addr_i == DBGCore.LOG_DATA))
259
260 # Set core stop on terminate. We'll be stopping some time *after*
261 # the offending instruction, at least until we can do back flushes
262 # that preserve NIA which we can't just yet.
263 with m.If(self.terminate_i):
264 sync += stopping.eq(1)
265 sync += terminated.eq(1)
266
267 comb += d_gpr.addr.eq(gspr_index)
268
269 # Core control signals generated by the debug module
270 comb += self.core_stop_o.eq((stopping & ~do_step) | self.terminate_i)
271 comb += self.core_rst_o.eq(do_reset)
272 comb += self.icache_rst_o.eq(do_icreset)
273 comb += self.terminated_o.eq(terminated | self.terminate_i)
274
275 # Logging RAM (none)
276
277 if self.LOG_LENGTH == 0:
278 self.log_read_data_o.eq(0)
279 self.log_write_addr_o.eq(0x00000001)
280
281 return m
282
283 # TODO: debug logging
284 """
285 maybe_log: with m.If(LOG_LENGTH > 0 generate
286 subtype log_ptr_t is unsigned(LOG_INDEX_BITS - 1 downto 0)
287 type log_array_t is array(0 to LOG_LENGTH - 1) of std_ulogic_vector(255 downto 0)
288 signal log_array : log_array_t
289 signal log_rd_ptr : log_ptr_t
290 signal log_wr_ptr : log_ptr_t
291 signal log_toggle = Signal()
292 signal log_wr_enable = Signal()
293 signal log_rd_ptr_latched : log_ptr_t
294 signal log_rd = Signal()_vector(255 downto 0)
295 signal log_dmi_reading = Signal()
296 signal log_dmi_read_done = Signal()
297
298 function select_dword(data = Signal()_vector(255 downto 0)
299 addr = Signal()_vector(31 downto 0)) return std_ulogic_vector is
300 variable firstbit : integer
301 begin
302 firstbit := to_integer(unsigned(addr(1 downto 0))) * 64
303 return data(firstbit + 63 downto firstbit)
304 end
305
306 attribute ram_style : string
307 attribute ram_style of log_array : signal is "block"
308 attribute ram_decomp : string
309 attribute ram_decomp of log_array : signal is "power"
310
311 begin
312 # Use MSB of read addresses to stop the logging
313 log_wr_enable.eq(not (self.log_read_addr(31) or log_dmi_addr(31))
314
315 log_ram: process(clk)
316 begin
317 with m.If(rising_edge(clk)):
318 with m.If(log_wr_enable = '1'):
319 log_array(to_integer(log_wr_ptr)).eq(self.log_data
320 end if
321 log_rd.eq(log_array(to_integer(log_rd_ptr_latched))
322 end if
323 end process
324
325
326 log_buffer: process(clk)
327 variable b : integer
328 variable data = Signal()_vector(255 downto 0)
329 begin
330 with m.If(rising_edge(clk)):
331 with m.If(rst = '1'):
332 log_wr_ptr.eq((others => '0')
333 log_toggle.eq('0'
334 with m.Elif(log_wr_enable = '1'):
335 with m.If(log_wr_ptr = to_unsigned(LOG_LENGTH - 1, LOG_INDEX_BITS)):
336 log_toggle.eq(not log_toggle
337 end if
338 log_wr_ptr.eq(log_wr_ptr + 1
339 end if
340 with m.If(do_dmi_log_rd = '1'):
341 log_rd_ptr_latched.eq(unsigned(log_dmi_addr(LOG_INDEX_BITS + 1 downto 2))
342 else
343 log_rd_ptr_latched.eq(unsigned(self.log_read_addr(LOG_INDEX_BITS + 1 downto 2))
344 end if
345 with m.If(log_dmi_read_done = '1'):
346 log_dmi_data.eq(select_dword(log_rd, log_dmi_addr)
347 else
348 self.log_read_data.eq(select_dword(log_rd, self.log_read_addr)
349 end if
350 log_dmi_read_done.eq(log_dmi_reading
351 log_dmi_reading.eq(do_dmi_log_rd
352 end if
353 end process
354 self.log_write_addr(LOG_INDEX_BITS - 1 downto 0).eq(std_ulogic_vector(log_wr_ptr)
355 self.log_write_addr(LOG_INDEX_BITS).eq('1'
356 self.log_write_addr(31 downto LOG_INDEX_BITS + 1).eq((others => '0')
357 end generate
358
359 """
360
361 def __iter__(self):
362 yield from self.dmi
363 yield self.core_stop_o
364 yield self.core_rst_o
365 yield self.icache_rst_o
366 yield self.terminate_i
367 yield self.core_stopped_i
368 yield from self.state
369 yield from self.d_gpr
370 yield from self.d_cr
371 yield from self.d_xer
372 yield self.log_data_i
373 yield self.log_read_addr_i
374 yield self.log_read_data_o
375 yield self.log_write_addr_o
376 yield self.terminated_o
377
378 def ports(self):
379 return list(self)
380
381
382 def test_debug():
383
384 dut = CoreDebug()
385 vl = rtlil.convert(dut, ports=dut.ports())
386 with open("test_core_debug.il", "w") as f:
387 f.write(vl)
388
389 if __name__ == '__main__':
390 test_debug()
391