Skip to content

Commit 96b3abe

Browse files
committed
Implement the Sdext debug mode extension.
The try_step API was changed to reflect the current debug mode state and to provide a mechanism for the external harness to request a change in that state (i.e. enter and exit debug mode). Two new callbacks indicate entry and exit from debug mode. This does not provide a model-external harness to connect to external debugger infrastructure; that is left for the future. Hook functions have been defined for behavior that is UNSPECIFIED in the debug specification or depends on the debugger's memory map (in riscv_sdext_hook_insts). StopCount is implemented for the single-hart case, with a note marking the change needed for multi-hart emulation. Some notes have been added under doc/ about the sdext implementation.
1 parent f4a94a3 commit 96b3abe

25 files changed

+784
-40
lines changed

c_emulator/riscv_callbacks.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,3 +137,13 @@ unit trap_callback(unit)
137137
}
138138
return UNIT;
139139
}
140+
141+
unit debug_entry_callback(unit)
142+
{
143+
return UNIT;
144+
}
145+
146+
unit debug_exit_callback(unit)
147+
{
148+
return UNIT;
149+
}

c_emulator/riscv_callbacks.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ unit csr_full_read_callback(const_sail_string csr_name, unsigned reg,
2323
unit vreg_write_callback(unsigned reg, lbits value);
2424
unit pc_write_callback(uint64_t value);
2525
unit trap_callback(unit);
26+
unit debug_entry_callback(unit);
27+
unit debug_exit_callback(unit);
2628

2729
// TODO: Move these implementations to C.
2830
unit zrvfi_write(uint64_t paddr, int64_t width, lbits value);

c_emulator/riscv_sim.cpp

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -543,7 +543,7 @@ void flush_logs(void)
543543

544544
void run_sail(void)
545545
{
546-
bool is_waiting;
546+
struct zstep_result step_result = {false, false, false, false};
547547
bool exit_wait = true;
548548
bool diverged = false;
549549

@@ -578,7 +578,12 @@ void run_sail(void)
578578
sail_int sail_step;
579579
CREATE(sail_int)(&sail_step);
580580
CONVERT_OF(sail_int, mach_int)(&sail_step, step_no);
581-
is_waiting = ztry_step(sail_step, exit_wait);
581+
582+
// If the model is in Debug mode, request an exit, else request
583+
// no change.
584+
enum zdebug_request dr
585+
= step_result.zin_debug_mode ? zDR_Resume : zDR_None;
586+
step_result = ztry_step(sail_step, exit_wait, dr);
582587
if (have_exception)
583588
goto step_exception;
584589
flush_logs();
@@ -587,7 +592,10 @@ void run_sail(void)
587592
rvfi->send_trace(config_print_rvfi);
588593
}
589594
}
590-
if (!is_waiting) {
595+
// TODO: better handling of entry into debug mode (step_result.zin_debug),
596+
// e.g, by connecting to a debugger interface.
597+
// For now, we just request an immediate resume above.
598+
if (!step_result.zin_wait) {
591599
if (config_print_step) {
592600
fprintf(trace_log, "\n");
593601
}

config/default.json

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,21 @@
225225
"Zvkt": {
226226
"supported": true
227227
},
228+
"Sdext": {
229+
"supported": true,
230+
"mem_base": 0,
231+
"mem_size": 1024,
232+
"trap_vector": 0,
233+
"writable_stepie": true,
234+
"stopcount": {
235+
"writable": true,
236+
"preset_value": false
237+
},
238+
"mprven": {
239+
"writable": true,
240+
"preset_value": true
241+
}
242+
},
228243
"Sscofpmf": {
229244
"supported": true
230245
},

doc/figs/Makefile

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
all: riscvspecdeps.svg riscvcsimdeps.svg
1+
all: riscvspecdeps.svg riscvcsimdeps.svg debug_mode_sm.svg
22

33
%.pdf: %.tex
44
pdflatex $<
@@ -7,5 +7,6 @@ all: riscvspecdeps.svg riscvcsimdeps.svg
77
dvisvgm -Z 1.5 --pdf $<
88

99
clean:
10-
rm riscvspecdeps.pdf riscvspecdeps.aux riscvspecdeps.log
11-
rm riscvcsimdeps.pdf riscvcsimdeps.aux riscvcsimdeps.log
10+
rm -f riscvspecdeps.pdf riscvspecdeps.aux riscvspecdeps.log
11+
rm -f riscvcsimdeps.pdf riscvcsimdeps.aux riscvcsimdeps.log
12+
rm -f debug_mode_sm.pdf debug_mode_sm.aux debug_mode_sm.log

doc/figs/debug_mode_sm.svg

Lines changed: 79 additions & 0 deletions
Loading

doc/figs/debug_mode_sm.tex

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
\documentclass{standalone}
2+
\usepackage{tikz}
3+
\usetikzlibrary{calc,positioning,backgrounds}
4+
\begin{document}
5+
\begin{tikzpicture}[
6+
% these two lines are needed for the svg to receive a white background.
7+
background rectangle/.style={rounded corners,fill=white},
8+
framed,
9+
% general settings
10+
node distance=6cm,
11+
align=center,
12+
% nodes
13+
base/.style={rectangle, rounded corners=3mm, minimum size=10mm, thick, draw=black},
14+
wait/.style={base, draw=black},
15+
debug/.style={base, draw=black, fill=red!30},
16+
% arrow labels
17+
label/.style={text=black},
18+
hltlabel/.style={text=red},
19+
reslabel/.style={text=green},
20+
% arrows
21+
enterwait/.style={black, thick},
22+
exitwait/.style={black, very thick},
23+
halt/.style={red, thick},
24+
resume/.style={green, thick},
25+
dbgarr/.style={black, thick},
26+
]
27+
28+
\node (dbg) [debug] {\textbf{Active}\\
29+
\textit{Debug Mode}};
30+
31+
\node (active) [base, above left of=dbg] {\textbf{Active}};
32+
\node (wait) [wait, above right of=dbg] {\textbf{Waiting}};
33+
34+
\node (dbg-cmd) [debug, below of=dbg] {\textbf{Active}\\
35+
\textit{Debug Mode}\\
36+
\textit{Abstract Command}};
37+
38+
% not used anymore.
39+
%\node (dbg-err) [debug, below right of=dbg] {\textbf{Active}\\
40+
% \textit{Debug Mode}\\
41+
% \textit{Abstract Command Error}};
42+
43+
% control for enterwait arrow
44+
\coordinate (nwaitloc) at ($(active)!0.5!(wait)+(0,0.5cm)$);
45+
% control for exitwait arrow
46+
\coordinate (xwaitloc) at ($(active)!0.5!(wait)-(0,0.5cm)$);
47+
48+
\draw[->,enterwait] (active) .. controls (nwaitloc) .. node[label,above]{\textbf{WFI,WRS}} (wait);
49+
\draw[->,exitwait] (wait) .. controls (xwaitloc) .. node[label,below]{\texttt{exit\_wait}} (active);
50+
51+
% control for halt-active arrow
52+
\coordinate (haltactloc) at ($(active)!0.5!(dbg)+(0.5cm,0.5cm)$);
53+
% control for resume arrow
54+
\coordinate (resumeloc) at ($(active)!0.5!(dbg)-(0.5cm,0.5cm)$);
55+
56+
\draw[->,halt] (active) .. controls (haltactloc) .. node[hltlabel,right]{\textbf{EBREAK}\\\texttt{DR\_halt}\\\texttt{dcsr.step}} (dbg);
57+
\draw[->,halt] (wait) -- node[hltlabel,right]{\texttt{DR\_halt}} (dbg);
58+
\draw[->,resume] (dbg) .. controls (resumeloc) .. node[reslabel,left]{\texttt{DR\_resume}\\\textbf{DRET}} (active);
59+
60+
% control for start-cmd arrow
61+
\coordinate (startcmdloc) at ($(dbg)!0.5!(dbg-cmd)+(0.5cm,0)$);
62+
% control for end-cmd arrow
63+
\coordinate (endcmdloc) at ($(dbg)!0.5!(dbg-cmd)-(0.5cm,0)$);
64+
65+
\draw[->,dbgarr] (dbg) .. controls (startcmdloc) .. node[label,right, near end]{\texttt{start\_debug\_abstract\_command}} (dbg-cmd);
66+
\draw[->,dbgarr] (dbg-cmd) .. controls (endcmdloc) .. node[label,left, near end]{\textbf{EBREAK}\\(Exception error)} (dbg);
67+
68+
\draw[->,resume] (dbg-cmd) edge [out=135,in=-135] node[reslabel,left]{\texttt{DR\_resume}} (active);
69+
70+
\end{tikzpicture}
71+
\end{document}

doc/notes_Debug_Mode.adoc

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
This is a brief description of the hart states involved in the
2+
implementation of the `Sdext` extension.
3+
4+
image::./figs/debug_mode_sm.svg[align="center"]
5+
6+
Under normal (non-Debug mode) operation, the `hart_state` is either
7+
`Active` or `Waiting`. The `Waiting` state is entered by the `WFI`
8+
and `WRS.{NTO,STO}` instructions, and exited due to reasons such as
9+
incoming interrupts or reservations becoming invalid.
10+
11+
Debug Mode creates two additional states for the hart that are
12+
variants of the `Active` state. The `Active (Debug Mode)` state is
13+
entered from the Active state either by executing an `EBREAK`, after
14+
executing an instruction in single-step mode (i.e. `dcsr[Step]` is
15+
set), or being requested to halt (`DR_halt`) by the external harness
16+
driving the model. If the halt request is made when the hart is
17+
`Waiting`, the hart completes the execution of the waiting instruction
18+
and enters the `Active (Debug Mode)` state.
19+
20+
When in `Active (Debug Mode)`, the debugger might request the
21+
execution of abstract commands (which may include the execution of
22+
instructions from a debug program buffer). The hart enters the
23+
`Active (Debug Mode Abstract Command)` state to perform this
24+
execution. This state is left either by executing an `EBREAK` or
25+
encountering an exception during execution; in both cases, the hart
26+
transitions back to `Active (Debug Mode)`. An error condition is set
27+
in the latter exception case; this should be checked (and then
28+
cleared) by the external harness.
29+
30+
The external harness can also request the model to exit the Debug mode
31+
states and re-enter `Active` with `DR_resume`.
32+
33+
The `DR_halt` and `DR_resume` requests to the model are made using
34+
arguments to the `try_step()` function that serves as the primary
35+
interface between the model and external harness.

model/CMakeLists.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ foreach (xlen IN ITEMS 32 64)
6565
)
6666

6767
set(sail_default_inst
68+
"riscv_sdext_inst_hooks.sail"
6869
"riscv_insts_base.sail"
6970
"riscv_insts_zifencei.sail"
7071
"riscv_insts_zaamo.sail"
@@ -79,6 +80,7 @@ foreach (xlen IN ITEMS 32 64)
7980
"riscv_insts_dext.sail"
8081
"riscv_insts_zcd.sail"
8182
"riscv_insts_svinval.sail"
83+
"riscv_insts_sdext.sail"
8284
"riscv_insts_zba.sail"
8385
"riscv_insts_zbb.sail"
8486
"riscv_insts_zbc.sail"
@@ -142,6 +144,7 @@ foreach (xlen IN ITEMS 32 64)
142144
"riscv_fdext_control.sail"
143145
"riscv_smcntrpmf.sail"
144146
"riscv_sys_reservation.sail"
147+
"riscv_sdext_control.sail"
145148
"riscv_sys_control.sail"
146149
)
147150

@@ -176,6 +179,7 @@ foreach (xlen IN ITEMS 32 64)
176179
"riscv_regs.sail"
177180
"riscv_pc_access.sail"
178181
"riscv_sys_regs.sail"
182+
"riscv_sdext_regs.sail"
179183
"riscv_pmp_regs.sail"
180184
"riscv_pmp_control.sail"
181185
"riscv_ext_regs.sail"

model/riscv_callbacks.sail

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,12 @@ function csr_full_read_callback(_) = ()
3131
val trap_callback = pure {c: "trap_callback"} : unit -> unit
3232
function trap_callback(_) = ()
3333

34+
val debug_entry_callback = pure {c: "debug_entry_callback"} : unit -> unit
35+
function debug_entry_callback(_) = ()
36+
37+
val debug_exit_callback = pure {c: "debug_exit_callback"} : unit -> unit
38+
function debug_exit_callback(_) = ()
39+
3440
// Overloads for easier use of callbacks
3541
function csr_name_write_callback(name : string, value : xlenbits) -> unit = {
3642
let csr = csr_name_map(name);

model/riscv_extensions.sail

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,10 @@ enum clause extension = Ext_Smcntrpmf
329329
mapping clause extensionName = Ext_Smcntrpmf <-> "smcntrpmf"
330330
function clause hartSupports(Ext_Smcntrpmf) = config extensions.Smcntrpmf.supported
331331

332+
// Debug
333+
enum clause extension = Ext_Sdext
334+
function clause hartSupports(Ext_Sdext) = config extensions.Sdext.supported
335+
332336
let extensions_ordered_for_isa_string = [
333337
// Single letter extensions.
334338
Ext_M,

model/riscv_inst_retire.sail

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ union ExecutionResult = {
2020
Illegal_Instruction : unit,
2121
Trap : (Privilege, ctl_result, xlenbits),
2222
Memory_Exception : (virtaddr, ExceptionType),
23+
Enter_Debug_Mode : debug_entry_cause,
24+
Exit_Debug_Abstract_Command : unit,
25+
Exit_Debug_Mode : unit,
2326

2427
// Did not retire for custom reason.
2528
Ext_CSR_Check_Failure : unit,

model/riscv_insts_base.sail

Lines changed: 44 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,16 @@ mapping clause encdec = UTYPE(imm, rd, op)
2525

2626
function clause execute (UTYPE(imm, rd, op)) = {
2727
let off : xlenbits = sign_extend(imm @ 0x000);
28-
X(rd) = match op {
29-
LUI => off,
30-
AUIPC => get_arch_pc() + off
31-
};
32-
RETIRE_SUCCESS
28+
match op {
29+
LUI => { X(rd) = off; RETIRE_SUCCESS },
30+
AUIPC => {
31+
if in_debug_abstract_command() & ~(debug_is_pc_instr_legal())
32+
then return Illegal_Instruction();
33+
34+
X(rd) = get_arch_pc() + off;
35+
RETIRE_SUCCESS
36+
},
37+
}
3338
}
3439

3540
mapping utype_mnemonic : uop <-> string = {
@@ -64,6 +69,8 @@ function clause execute (JAL(imm, rd)) = {
6469
match ext_control_check_pc(target) {
6570
Ext_ControlAddr_Error(e) => Ext_ControlAddr_Check_Failure(e),
6671
Ext_ControlAddr_OK(target) => {
72+
if in_debug_abstract_command() & ~(debug_is_legal_ct_target(target))
73+
then return Illegal_Instruction();
6774
/* Perform standard alignment check */
6875
let target_bits = bits_of(target);
6976
if bit_to_bool(target_bits[1]) & not(currentlyEnabled(Ext_Zca)) then {
@@ -123,6 +130,9 @@ function clause execute (BTYPE(imm, rs2, rs1, op)) = {
123130
match ext_control_check_pc(target) {
124131
Ext_ControlAddr_Error(e) => Ext_ControlAddr_Check_Failure(e),
125132
Ext_ControlAddr_OK(target) => {
133+
if in_debug_abstract_command() & ~(debug_is_legal_ct_target(target))
134+
then return Illegal_Instruction();
135+
126136
let target_bits = bits_of(target);
127137
if bit_to_bool(target_bits[1]) & not(currentlyEnabled(Ext_Zca)) then {
128138
Memory_Exception(target, E_Fetch_Addr_Align())
@@ -521,6 +531,9 @@ mapping clause encdec = ECALL()
521531
<-> 0b000000000000 @ 0b00000 @ 0b000 @ 0b00000 @ 0b1110011
522532

523533
function clause execute ECALL() = {
534+
if in_debug_abstract_command()
535+
then return debug_mode_ecall_hook();
536+
524537
let t : sync_exception =
525538
struct { trap = match (cur_privilege) {
526539
User => E_U_EnvCall(),
@@ -541,6 +554,9 @@ mapping clause encdec = MRET()
541554
<-> 0b0011000 @ 0b00010 @ 0b00000 @ 0b000 @ 0b00000 @ 0b1110011
542555

543556
function clause execute MRET() = {
557+
if in_debug_abstract_command()
558+
then return debug_mode_mret_hook();
559+
544560
if cur_privilege != Machine
545561
then Illegal_Instruction()
546562
else if not(ext_check_xret_priv(Machine))
@@ -560,6 +576,9 @@ mapping clause encdec = SRET()
560576
<-> 0b0001000 @ 0b00010 @ 0b00000 @ 0b000 @ 0b00000 @ 0b1110011
561577

562578
function clause execute SRET() = {
579+
if in_debug_abstract_command()
580+
then return debug_mode_sret_hook();
581+
563582
let sret_illegal : bool = match cur_privilege {
564583
User => true,
565584
Supervisor => not(currentlyEnabled(Ext_S)) | mstatus[TSR] == 0b1,
@@ -583,8 +602,24 @@ union clause ast = EBREAK : unit
583602
mapping clause encdec = EBREAK()
584603
<-> 0b000000000001 @ 0b00000 @ 0b000 @ 0b00000 @ 0b1110011
585604

605+
function ebreak_into_debug() -> bool =
606+
if currentlyEnabled(Ext_Sdext)
607+
then match cur_privilege {
608+
User => bool_bits(dcsr[EBreakU]),
609+
Supervisor => bool_bits(dcsr[EBreakS]),
610+
Machine => bool_bits(dcsr[EBreakM]),
611+
// TODO: VS/VU modes
612+
}
613+
else false
614+
586615
function clause execute EBREAK() =
587-
Memory_Exception(Virtaddr(PC), E_Breakpoint())
616+
if in_debug_abstract_command() then {
617+
Exit_Debug_Abstract_Command()
618+
} else if ebreak_into_debug() then {
619+
Enter_Debug_Mode(DBGEntry_EBreak)
620+
} else {
621+
Memory_Exception(Virtaddr(PC), E_Breakpoint())
622+
}
588623

589624
mapping clause assembly = EBREAK() <-> "ebreak"
590625

@@ -595,7 +630,9 @@ mapping clause encdec = WFI()
595630
<-> 0b000100000101 @ 0b00000 @ 0b000 @ 0b00000 @ 0b1110011
596631

597632
function clause execute WFI() =
598-
match cur_privilege {
633+
if in_debug_abstract_command() | in_single_step()
634+
then RETIRE_SUCCESS
635+
else match cur_privilege {
599636
Machine => Enter_Wait(WAIT_WFI),
600637
Supervisor => if mstatus[TW] == 0b1
601638
then Illegal_Instruction()

model/riscv_insts_sdext.sail

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
/*=======================================================================================*/
2+
/* This Sail RISC-V architecture model, comprising all files and */
3+
/* directories except where otherwise noted is subject the BSD */
4+
/* two-clause license in the LICENSE file. */
5+
/* */
6+
/* SPDX-License-Identifier: BSD-2-Clause */
7+
/*=======================================================================================*/
8+
9+
/* This file specifies the `dret` instruction in the `Sdext` extension. */
10+
11+
union clause ast = DRET : unit
12+
13+
mapping clause encdec = DRET()
14+
<-> 0b01111011001000000000000001110011
15+
when currentlyEnabled(Ext_Sdext) & ~(in_debug_abstract_command())
16+
17+
function clause execute DRET() =
18+
Exit_Debug_Mode()

0 commit comments

Comments
 (0)