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