forked from antonblanchard/microwatt
-
Notifications
You must be signed in to change notification settings - Fork 0
/
core_debug.vhdl
443 lines (397 loc) · 17.7 KB
/
core_debug.vhdl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
library work;
use work.utils.all;
use work.common.all;
use work.wishbone_types.all;
entity core_debug is
generic (
-- Length of log buffer
LOG_LENGTH : natural := 512
);
port (
clk : in std_logic;
rst : in std_logic;
dmi_addr : in std_ulogic_vector(3 downto 0);
dmi_din : in std_ulogic_vector(63 downto 0);
dmi_dout : out std_ulogic_vector(63 downto 0);
dmi_req : in std_ulogic;
dmi_wr : in std_ulogic;
dmi_ack : out std_ulogic;
-- Debug actions
core_stop : out std_ulogic;
core_rst : out std_ulogic;
icache_rst : out std_ulogic;
-- Core status inputs
terminate : in std_ulogic;
core_stopped : in std_ulogic;
nia : in std_ulogic_vector(63 downto 0);
msr : in std_ulogic_vector(63 downto 0);
wb_snoop_in : in wishbone_master_out := wishbone_master_out_init;
-- GPR/FPR register read port
dbg_gpr_req : out std_ulogic;
dbg_gpr_ack : in std_ulogic;
dbg_gpr_addr : out gspr_index_t;
dbg_gpr_data : in std_ulogic_vector(63 downto 0);
-- SPR register read port for SPRs in execute1
dbg_spr_req : out std_ulogic;
dbg_spr_ack : in std_ulogic;
dbg_spr_addr : out std_ulogic_vector(7 downto 0);
dbg_spr_data : in std_ulogic_vector(63 downto 0);
-- SPR register read port for SPRs in loadstore1 and mmu
dbg_ls_spr_req : out std_ulogic;
dbg_ls_spr_ack : in std_ulogic;
dbg_ls_spr_addr : out std_ulogic_vector(1 downto 0);
dbg_ls_spr_data : in std_ulogic_vector(63 downto 0);
-- Core logging data
log_data : in std_ulogic_vector(255 downto 0);
log_read_addr : in std_ulogic_vector(31 downto 0);
log_read_data : out std_ulogic_vector(63 downto 0);
log_write_addr : out std_ulogic_vector(31 downto 0);
-- Misc
terminated_out : out std_ulogic
);
end core_debug;
architecture behave of core_debug is
-- DMI needs fixing... make a one clock pulse
signal dmi_req_1: std_ulogic;
-- CTRL register (direct actions, write 1 to act, read back 0)
-- bit 0 : Core stop
-- bit 1 : Core reset (doesn't clear stop)
-- bit 2 : Icache reset
-- bit 3 : Single step
-- bit 4 : Core start
constant DBG_CORE_CTRL : std_ulogic_vector(3 downto 0) := "0000";
constant DBG_CORE_CTRL_STOP : integer := 0;
constant DBG_CORE_CTRL_RESET : integer := 1;
constant DBG_CORE_CTRL_ICRESET : integer := 2;
constant DBG_CORE_CTRL_STEP : integer := 3;
constant DBG_CORE_CTRL_START : integer := 4;
-- STAT register (read only)
-- bit 0 : Core stopping (wait til bit 1 set)
-- bit 1 : Core stopped
-- bit 2 : Core terminated (clears with start or reset)
constant DBG_CORE_STAT : std_ulogic_vector(3 downto 0) := "0001";
constant DBG_CORE_STAT_STOPPING : integer := 0;
constant DBG_CORE_STAT_STOPPED : integer := 1;
constant DBG_CORE_STAT_TERM : integer := 2;
-- NIA register (read only for now)
constant DBG_CORE_NIA : std_ulogic_vector(3 downto 0) := "0010";
-- MSR (read only)
constant DBG_CORE_MSR : std_ulogic_vector(3 downto 0) := "0011";
-- GSPR register index
constant DBG_CORE_GSPR_INDEX : std_ulogic_vector(3 downto 0) := "0100";
-- GSPR register data
constant DBG_CORE_GSPR_DATA : std_ulogic_vector(3 downto 0) := "0101";
-- Log buffer address and data registers
constant DBG_CORE_LOG_ADDR : std_ulogic_vector(3 downto 0) := "0110";
constant DBG_CORE_LOG_DATA : std_ulogic_vector(3 downto 0) := "0111";
constant DBG_CORE_LOG_TRIGGER : std_ulogic_vector(3 downto 0) := "1000";
constant DBG_CORE_LOG_MTRIGGER : std_ulogic_vector(3 downto 0) := "1001";
constant LOG_INDEX_BITS : natural := log2(LOG_LENGTH);
-- Some internal wires
signal stat_reg : std_ulogic_vector(63 downto 0);
-- Some internal latches
signal stopping : std_ulogic;
signal do_step : std_ulogic;
signal do_reset : std_ulogic;
signal do_icreset : std_ulogic;
signal terminated : std_ulogic;
signal do_gspr_rd : std_ulogic;
signal gspr_index : std_ulogic_vector(7 downto 0);
signal gspr_data : std_ulogic_vector(63 downto 0);
signal spr_index_valid : std_ulogic;
signal log_dmi_addr : std_ulogic_vector(31 downto 0) := (others => '0');
signal log_dmi_data : std_ulogic_vector(63 downto 0) := (others => '0');
signal log_dmi_trigger : std_ulogic_vector(63 downto 0) := (others => '0');
signal log_mem_trigger : std_ulogic_vector(63 downto 0) := (others => '0');
signal do_log_trigger : std_ulogic := '0';
signal do_log_mtrigger : std_ulogic := '0';
signal trigger_was_log : std_ulogic := '0';
signal trigger_was_mem : std_ulogic := '0';
signal do_dmi_log_rd : std_ulogic;
signal dmi_read_log_data : std_ulogic;
signal dmi_read_log_data_1 : std_ulogic;
signal log_trigger_delay : integer range 0 to 255 := 0;
begin
-- Single cycle register accesses on DMI except for GSPR data
dmi_ack <= dmi_req when dmi_addr /= DBG_CORE_GSPR_DATA
else dbg_gpr_ack or dbg_spr_ack or dbg_ls_spr_ack;
-- Status register read composition
stat_reg <= (2 => terminated,
1 => core_stopped,
0 => stopping,
others => '0');
gspr_data <= dbg_gpr_data when gspr_index(5) = '0' else
dbg_ls_spr_data when dbg_ls_spr_req = '1' else
dbg_spr_data when spr_index_valid = '1' else
(others => '0');
-- DMI read data mux
with dmi_addr select dmi_dout <=
stat_reg when DBG_CORE_STAT,
nia when DBG_CORE_NIA,
msr when DBG_CORE_MSR,
gspr_data when DBG_CORE_GSPR_DATA,
log_write_addr & log_dmi_addr when DBG_CORE_LOG_ADDR,
log_dmi_data when DBG_CORE_LOG_DATA,
log_dmi_trigger when DBG_CORE_LOG_TRIGGER,
log_mem_trigger when DBG_CORE_LOG_MTRIGGER,
(others => '0') when others;
-- DMI writes
reg_write: process(clk)
begin
if rising_edge(clk) then
-- Reset the 1-cycle "do" signals
do_step <= '0';
do_reset <= '0';
do_icreset <= '0';
do_dmi_log_rd <= '0';
if (rst) then
stopping <= '0';
terminated <= '0';
log_trigger_delay <= 0;
gspr_index <= (others => '0');
log_dmi_addr <= (others => '0');
trigger_was_log <= '0';
trigger_was_mem <= '0';
else
if do_log_trigger = '1' or do_log_mtrigger = '1' or log_trigger_delay /= 0 then
if log_trigger_delay = 255 or
(LOG_LENGTH < 1024 and log_trigger_delay = LOG_LENGTH / 4) then
log_dmi_trigger(1) <= trigger_was_log;
log_mem_trigger(1) <= trigger_was_mem;
log_trigger_delay <= 0;
trigger_was_log <= '0';
trigger_was_mem <= '0';
else
log_trigger_delay <= log_trigger_delay + 1;
end if;
end if;
if do_log_trigger = '1' then
trigger_was_log <= '1';
end if;
if do_log_mtrigger = '1' then
trigger_was_mem <= '1';
end if;
-- Edge detect on dmi_req for 1-shot pulses
dmi_req_1 <= dmi_req;
if dmi_req = '1' and dmi_req_1 = '0' then
if dmi_wr = '1' then
report("DMI write to " & to_hstring(dmi_addr));
-- Control register actions
if dmi_addr = DBG_CORE_CTRL then
if dmi_din(DBG_CORE_CTRL_RESET) = '1' then
do_reset <= '1';
terminated <= '0';
end if;
if dmi_din(DBG_CORE_CTRL_STOP) = '1' then
stopping <= '1';
end if;
if dmi_din(DBG_CORE_CTRL_STEP) = '1' then
do_step <= '1';
terminated <= '0';
end if;
if dmi_din(DBG_CORE_CTRL_ICRESET) = '1' then
do_icreset <= '1';
end if;
if dmi_din(DBG_CORE_CTRL_START) = '1' then
stopping <= '0';
terminated <= '0';
end if;
elsif dmi_addr = DBG_CORE_GSPR_INDEX then
gspr_index <= dmi_din(7 downto 0);
elsif dmi_addr = DBG_CORE_LOG_ADDR then
log_dmi_addr <= dmi_din(31 downto 0);
do_dmi_log_rd <= '1';
elsif dmi_addr = DBG_CORE_LOG_TRIGGER then
log_dmi_trigger <= dmi_din;
elsif dmi_addr = DBG_CORE_LOG_MTRIGGER then
log_mem_trigger <= dmi_din;
end if;
else
report("DMI read from " & to_string(dmi_addr));
end if;
elsif dmi_read_log_data = '0' and dmi_read_log_data_1 = '1' then
-- Increment log_dmi_addr after the end of a read from DBG_CORE_LOG_DATA
log_dmi_addr(LOG_INDEX_BITS + 1 downto 0) <=
std_ulogic_vector(unsigned(log_dmi_addr(LOG_INDEX_BITS+1 downto 0)) + 1);
do_dmi_log_rd <= '1';
end if;
dmi_read_log_data_1 <= dmi_read_log_data;
if dmi_req = '1' and dmi_addr = DBG_CORE_LOG_DATA then
dmi_read_log_data <= '1';
else
dmi_read_log_data <= '0';
end if;
-- Set core stop on terminate. We'll be stopping some time *after*
-- the offending instruction, at least until we can do back flushes
-- that preserve NIA which we can't just yet.
if terminate = '1' then
stopping <= '1';
terminated <= '1';
end if;
end if;
end if;
end process;
gspr_access: process(clk)
variable valid : std_ulogic;
variable sel : spr_selector;
variable isram : std_ulogic;
variable raddr : ramspr_index;
variable odd : std_ulogic;
begin
if rising_edge(clk) then
dbg_gpr_req <= '0';
dbg_spr_req <= '0';
dbg_ls_spr_req <= '0';
if rst = '0' and dmi_req = '1' and dmi_addr = DBG_CORE_GSPR_DATA then
if gspr_index(5) = '0' then
dbg_gpr_req <= '1';
elsif gspr_index(4 downto 2) = "111" then
dbg_ls_spr_req <= '1';
else
dbg_spr_req <= '1';
end if;
end if;
-- Map 0 - 0x1f to GPRs, 0x20 - 0x3f to SPRs, and 0x40 - 0x5f to FPRs
dbg_gpr_addr <= gspr_index(6) & gspr_index(4 downto 0);
dbg_ls_spr_addr <= gspr_index(1 downto 0);
-- For SPRs, use the same mapping as when the fast SPRs were in the GPR file
valid := '1';
sel := "000";
isram := '1';
raddr := (others => '0');
odd := '0';
case gspr_index(4 downto 0) is
when 5x"00" =>
raddr := RAMSPR_LR;
when 5x"01" =>
odd := '1';
raddr := RAMSPR_CTR;
when 5x"02" | 5x"03" =>
odd := gspr_index(0);
raddr := RAMSPR_SRR0;
when 5x"04" | 5x"05" =>
odd := gspr_index(0);
raddr := RAMSPR_HSRR0;
when 5x"06" | 5x"07" =>
odd := gspr_index(0);
raddr := RAMSPR_SPRG0;
when 5x"08" | 5x"09" =>
odd := gspr_index(0);
raddr := RAMSPR_SPRG2;
when 5x"0a" | 5x"0b" =>
odd := gspr_index(0);
raddr := RAMSPR_HSPRG0;
when 5x"0c" =>
isram := '0';
sel := SPRSEL_XER;
when 5x"0d" =>
raddr := RAMSPR_TAR;
when others =>
valid := '0';
end case;
dbg_spr_addr <= isram & sel & std_ulogic_vector(raddr) & odd;
spr_index_valid <= valid;
end if;
end process;
-- Core control signals generated by the debug module
core_stop <= stopping and not do_step;
core_rst <= do_reset;
icache_rst <= do_icreset;
terminated_out <= terminated;
-- Logging RAM
maybe_log: if LOG_LENGTH > 0 generate
subtype log_ptr_t is unsigned(LOG_INDEX_BITS - 1 downto 0);
type log_array_t is array(0 to LOG_LENGTH - 1) of std_ulogic_vector(255 downto 0);
signal log_array : log_array_t;
signal log_rd_ptr : log_ptr_t;
signal log_wr_ptr : log_ptr_t;
signal log_toggle : std_ulogic;
signal log_wr_enable : std_ulogic;
signal log_rd_ptr_latched : log_ptr_t;
signal log_rd : std_ulogic_vector(255 downto 0);
signal log_dmi_reading : std_ulogic;
signal log_dmi_read_done : std_ulogic;
function select_dword(data : std_ulogic_vector(255 downto 0);
addr : std_ulogic_vector(31 downto 0)) return std_ulogic_vector is
variable firstbit : integer;
begin
assert not is_X(addr);
firstbit := to_integer(unsigned(addr(1 downto 0))) * 64;
return data(firstbit + 63 downto firstbit);
end;
attribute ram_style : string;
attribute ram_style of log_array : signal is "block";
attribute ram_decomp : string;
attribute ram_decomp of log_array : signal is "power";
begin
-- Use MSB of read addresses to stop the logging
log_wr_enable <= not (log_read_addr(31) or log_dmi_addr(31) or log_dmi_trigger(1) or log_mem_trigger(1));
log_ram: process(clk)
begin
if rising_edge(clk) then
if log_wr_enable = '1' then
assert not is_X(log_wr_ptr);
log_array(to_integer(log_wr_ptr)) <= log_data;
end if;
if is_X(log_rd_ptr_latched) then
log_rd <= (others => 'X');
else
log_rd <= log_array(to_integer(log_rd_ptr_latched));
end if;
end if;
end process;
log_buffer: process(clk)
variable b : integer;
variable data : std_ulogic_vector(255 downto 0);
begin
if rising_edge(clk) then
if rst = '1' then
log_wr_ptr <= (others => '0');
log_toggle <= '0';
log_rd_ptr_latched <= (others => '0');
elsif log_wr_enable = '1' then
if log_wr_ptr = to_unsigned(LOG_LENGTH - 1, LOG_INDEX_BITS) then
log_toggle <= not log_toggle;
end if;
log_wr_ptr <= log_wr_ptr + 1;
end if;
if do_dmi_log_rd = '1' then
log_rd_ptr_latched <= unsigned(log_dmi_addr(LOG_INDEX_BITS + 1 downto 2));
else
log_rd_ptr_latched <= unsigned(log_read_addr(LOG_INDEX_BITS + 1 downto 2));
end if;
if log_dmi_read_done = '1' then
log_dmi_data <= select_dword(log_rd, log_dmi_addr);
else
log_read_data <= select_dword(log_rd, log_read_addr);
end if;
log_dmi_read_done <= log_dmi_reading;
log_dmi_reading <= do_dmi_log_rd;
do_log_trigger <= '0';
if log_data(42) = log_dmi_trigger(63) and
log_data(41 downto 0) = log_dmi_trigger(43 downto 2) and
log_dmi_trigger(0) = '1' then
do_log_trigger <= '1';
end if;
do_log_mtrigger <= '0';
if (wb_snoop_in.cyc and wb_snoop_in.stb and wb_snoop_in.we) = '1' and
wb_snoop_in.adr = log_mem_trigger(wishbone_addr_bits + wishbone_log2_width - 1
downto wishbone_log2_width) and
log_mem_trigger(0) = '1' then
do_log_mtrigger <= '1';
end if;
end if;
end process;
log_write_addr(LOG_INDEX_BITS - 1 downto 0) <= std_ulogic_vector(log_wr_ptr);
log_write_addr(LOG_INDEX_BITS) <= '1';
log_write_addr(31 downto LOG_INDEX_BITS + 1) <= (others => '0');
end generate;
no_log: if LOG_LENGTH = 0 generate
begin
log_read_data <= (others => '0');
log_write_addr <= x"00000001";
end generate;
end behave;