10
10
11
11
register hart_state : HartState = HART_ACTIVE()
12
12
13
- // Returns whether the retire result returned by the execution of an
14
- // instruction counts as a step, where a step is said to occur when an
15
- // instruction retires successfully or it raises an exception.
16
- // Crucially, if an instruction returns a Wait retire result, it does
17
- // not step.
18
- function retires_or_traps(r : ExecutionResult(Retire_Failure)) -> bool =
19
- match r {
20
- RETIRE_OK() => true,
21
- RETIRE_FAIL(Illegal_Instruction()) => true,
22
- RETIRE_FAIL(Memory_Exception(_)) => true,
23
- RETIRE_FAIL(Trap(_)) => true,
24
-
25
- // do not step if a wait instruction executed.
26
- RETIRE_FAIL(Wait_For_Interrupt()) => false,
27
-
28
- // errors from extensions
29
- RETIRE_FAIL(Ext_ControlAddr_Check_Failure(_)) => true,
30
- RETIRE_FAIL(Ext_DataAddr_Check_Failure(_)) => true,
31
- RETIRE_FAIL(Ext_CSR_Check_Failure()) => true,
32
- RETIRE_FAIL(Ext_XRET_Priv_Failure(_)) => true,
33
- }
34
-
35
13
union Step = {
36
14
Step_Pending_Interrupt : (InterruptType, Privilege),
37
15
Step_Ext_Fetch_Failure : ext_fetch_addr_error,
@@ -40,15 +18,15 @@ union Step = {
40
18
Step_Waiting : unit,
41
19
}
42
20
43
- function run_hart_waiting(step_no : int , exit_wait : bool, instbits : instbits) -> ( Step, bool) = {
21
+ function run_hart_waiting(step_no : nat , exit_wait : bool, instbits : instbits) -> Step = {
44
22
if shouldWakeForInterrupt() then {
45
23
if get_config_print_instr()
46
24
then print_instr("interrupt exit from WAIT state at PC " ^ BitStr(PC));
47
25
48
26
hart_state = HART_ACTIVE();
49
27
// The waiting instruction retires successfully. The
50
28
// pending interrupts will be handled in the next step.
51
- ( Step_Execute(RETIRE_OK(), instbits), true )
29
+ Step_Execute(RETIRE_OK(), instbits)
52
30
} else if exit_wait then {
53
31
// There are no pending interrupts; transition out of the Wait
54
32
// as instructed.
@@ -61,27 +39,27 @@ function run_hart_waiting(step_no : int, exit_wait : bool, instbits : instbits)
61
39
// implementation-specific, bounded time limit, the WFI
62
40
// instruction causes an illegal-instruction exception."
63
41
if (cur_privilege == Machine | mstatus[TW] == 0b0)
64
- then ( Step_Execute(RETIRE_OK(), instbits), true )
65
- else ( Step_Execute(RETIRE_FAIL(Illegal_Instruction()), instbits), true )
42
+ then Step_Execute(RETIRE_OK(), instbits)
43
+ else Step_Execute(RETIRE_FAIL(Illegal_Instruction()), instbits)
66
44
} else {
67
45
if get_config_print_instr()
68
46
then print_instr("remaining in WAIT state at PC " ^ BitStr(PC));
69
- ( Step_Waiting(), false )
47
+ Step_Waiting()
70
48
}
71
49
}
72
50
73
- function run_hart_active(step_no: int, exit_wait : bool ) -> ( Step, bool) = {
51
+ function run_hart_active(step_no: nat ) -> Step = {
74
52
match dispatchInterrupt(cur_privilege) {
75
- Some(intr, priv) => ( Step_Pending_Interrupt(intr, priv), false ),
53
+ Some(intr, priv) => Step_Pending_Interrupt(intr, priv),
76
54
None() => match ext_fetch_hook(fetch()) {
77
55
/* extension error */
78
- F_Ext_Error(e) => ( Step_Ext_Fetch_Failure(e), false ),
56
+ F_Ext_Error(e) => Step_Ext_Fetch_Failure(e),
79
57
/* standard error */
80
- F_Error(e, addr) => ( Step_Fetch_Failure(Virtaddr(addr), e), false ),
58
+ F_Error(e, addr) => Step_Fetch_Failure(Virtaddr(addr), e),
81
59
/* non-error cases: */
82
60
F_RVC(h) => {
83
61
sail_instr_announce(h);
84
- let instbits : xlenbits = zero_extend(h);
62
+ let instbits : instbits = zero_extend(h);
85
63
let ast = ext_decode_compressed(h);
86
64
if get_config_print_instr()
87
65
then {
@@ -91,9 +69,9 @@ function run_hart_active(step_no: int, exit_wait : bool) -> (Step, bool) = {
91
69
if currentlyEnabled(Ext_Zca) then {
92
70
nextPC = PC + 2;
93
71
let r = execute(ast);
94
- ( Step_Execute(r, instbits), retires_or_traps(r) )
72
+ Step_Execute(r, instbits)
95
73
} else {
96
- ( Step_Execute(RETIRE_FAIL(Illegal_Instruction()), instbits), true )
74
+ Step_Execute(RETIRE_FAIL(Illegal_Instruction()), instbits)
97
75
}
98
76
},
99
77
F_Base(w) => {
@@ -106,7 +84,7 @@ function run_hart_active(step_no: int, exit_wait : bool) -> (Step, bool) = {
106
84
};
107
85
nextPC = PC + 4;
108
86
let r = execute(ast);
109
- ( Step_Execute(r, instbits), retires_or_traps(r) )
87
+ Step_Execute(r, instbits)
110
88
}
111
89
}
112
90
}
@@ -117,12 +95,28 @@ function wfi_is_nop() -> bool = config platform.wfi_is_nop
117
95
// The `try_step` function is the main internal driver of the Sail
118
96
// model. It performs the fetch-decode-execute for an instruction. It
119
97
// is also the primary interface to the non-Sail execution harness.
120
- // The harness calls this function with the current step number (which
121
- // numbers the active or wait step), and whether it should exit a wait
122
- // state. The `try_step` function returns whether the Sail emulator
123
- // executed a step, and the stepper state at the end of the step.
124
-
125
- function try_step(step_no : int, exit_wait : bool) -> bool = {
98
+ //
99
+ // A "step" is a full execution of an instruction, resulting either
100
+ // in its retirement or a trap. WFI and WRS instructions can cause
101
+ // the model to wait (hart_state is HART_WAITING), in which case
102
+ // a step has not happened and `try_step()` returns false. Otherwise
103
+ // it returns true. Equivalently, it returns whether the model is now
104
+ // in an active state (HART_ACTIVE).
105
+ //
106
+ // * step_no: the current step number; this is maintained by by the
107
+ // non-Sail harness and is incremented when `try_step()`
108
+ // returns true.
109
+ // * exit_wait: if true, and the model is waiting (HART_WAITING)
110
+ // then it will wake up (switch to HART_ACTIVE) and
111
+ // complete the WFI/WRS instruction, either successfully
112
+ // retiring it or causing an illegal instruction
113
+ // exception depending on mstatus[TW]. `exit_wait`
114
+ // only affects the behaviour if the model is already
115
+ // waiting from a previous WFI/WRS. It doesn't affect
116
+ // WFI/WRS instructions executed in the same call of
117
+ // `try_step()` (but see `wfi_is_nop()`).
118
+ //
119
+ function try_step(step_no : nat, exit_wait : bool) -> bool = {
126
120
/* for step extensions */
127
121
ext_pre_step_hook();
128
122
@@ -136,95 +130,72 @@ function try_step(step_no : int, exit_wait : bool) -> bool = {
136
130
*/
137
131
minstret_increment = should_inc_minstret(cur_privilege);
138
132
139
- let ( step_val, did_step) : ( Step, bool) = match hart_state {
133
+ let step_val : Step = match hart_state {
140
134
HART_WAITING(instbits) => run_hart_waiting(step_no, exit_wait, instbits),
141
- HART_ACTIVE() => run_hart_active(step_no, exit_wait ),
135
+ HART_ACTIVE() => run_hart_active(step_no),
142
136
};
143
137
144
- var stepped : bool = did_step;
145
-
146
138
match step_val {
147
139
Step_Pending_Interrupt(intr, priv) => {
148
140
if get_config_print_instr()
149
141
then print_bits("Handling interrupt: ", interruptType_to_bits(intr));
150
- minstret_increment = false;
151
142
handle_interrupt(intr, priv)
152
143
},
153
- Step_Ext_Fetch_Failure(e) => {
154
- minstret_increment = false;
155
- ext_handle_fetch_check_error(e)
156
- },
157
- Step_Fetch_Failure(vaddr, e) => {
158
- minstret_increment = false;
159
- handle_mem_exception(vaddr, e)
160
- },
161
- Step_Waiting() => {
162
- assert(hart_is_waiting(hart_state), "cannot be Waiting in a non-Wait state")
163
- },
164
- Step_Execute(RETIRE_OK(), _) => {
165
- assert(hart_is_active(hart_state))
166
- },
144
+ Step_Ext_Fetch_Failure(e) => ext_handle_fetch_check_error(e),
145
+ Step_Fetch_Failure(vaddr, e) => handle_mem_exception(vaddr, e),
146
+ Step_Waiting() => assert(hart_is_waiting(hart_state), "cannot be Waiting in a non-Wait state"),
147
+ Step_Execute(RETIRE_OK(), _) => assert(hart_is_active(hart_state)),
167
148
// standard errors
168
- Step_Execute(RETIRE_FAIL(Trap(priv, ctl, pc)), _) => {
169
- minstret_increment = false;
170
- set_next_pc(exception_handler(priv, ctl, pc))
171
- },
172
- Step_Execute(RETIRE_FAIL(Memory_Exception(vaddr, e)), _) => {
173
- minstret_increment = false;
174
- handle_mem_exception(vaddr, e)
175
- },
176
- Step_Execute(RETIRE_FAIL(Illegal_Instruction()), instbits) => {
177
- minstret_increment = false;
178
- handle_illegal(instbits)
179
- },
149
+ Step_Execute(RETIRE_FAIL(Trap(priv, ctl, pc)), _) => set_next_pc(exception_handler(priv, ctl, pc)),
150
+ Step_Execute(RETIRE_FAIL(Memory_Exception(vaddr, e)), _) => handle_mem_exception(vaddr, e),
151
+ Step_Execute(RETIRE_FAIL(Illegal_Instruction()), instbits) => handle_illegal(instbits),
180
152
Step_Execute(RETIRE_FAIL(Wait_For_Interrupt()), instbits) =>
181
153
if wfi_is_nop() then {
182
154
// This is the same as the RETIRE_OK case.
183
155
assert(hart_is_active(hart_state));
184
- // Override the default step for WFI.
185
- stepped = true;
186
156
} else {
187
157
// Transition into the wait state.
188
158
if get_config_print_instr()
189
159
then print_instr("entering WAIT state at PC " ^ BitStr(PC));
190
160
hart_state = HART_WAITING(instbits);
191
161
},
192
162
// errors from extensions
193
- Step_Execute(RETIRE_FAIL(Ext_CSR_Check_Failure()), _) => {
194
- minstret_increment = false;
195
- ext_check_CSR_fail()
196
- },
197
- Step_Execute(RETIRE_FAIL(Ext_ControlAddr_Check_Failure(e)), _) => {
198
- minstret_increment = false;
199
- ext_handle_control_check_error(e)
200
- },
201
- Step_Execute(RETIRE_FAIL(Ext_DataAddr_Check_Failure(e)), _) => {
202
- minstret_increment = false;
203
- ext_handle_data_check_error(e)
204
- },
205
- Step_Execute(RETIRE_FAIL(Ext_XRET_Priv_Failure()), _) => {
206
- minstret_increment = false;
207
- ext_fail_xret_priv ()
208
- },
163
+ Step_Execute(RETIRE_FAIL(Ext_CSR_Check_Failure()), _) => ext_check_CSR_fail(),
164
+ Step_Execute(RETIRE_FAIL(Ext_ControlAddr_Check_Failure(e)), _) => ext_handle_control_check_error(e),
165
+ Step_Execute(RETIRE_FAIL(Ext_DataAddr_Check_Failure(e)), _) => ext_handle_data_check_error(e),
166
+ Step_Execute(RETIRE_FAIL(Ext_XRET_Priv_Failure()), _) => ext_fail_xret_priv(),
209
167
};
210
168
211
169
match hart_state {
212
- HART_WAITING(_) => () ,
170
+ HART_WAITING(_) => false ,
213
171
HART_ACTIVE() => {
214
172
tick_pc();
215
- update_minstret();
173
+
174
+ let retired : bool = match step_val {
175
+ Step_Execute(RETIRE_OK(), _) => true,
176
+ // WFI retires immediately if the model is configured to treat it as a nop.
177
+ // Otherwise it always waits for at least one call of `try_step()`.
178
+ Step_Execute(RETIRE_FAIL(Wait_For_Interrupt()), _) if wfi_is_nop() => true,
179
+ _ => false,
180
+ };
181
+
182
+ // Increment minstret if we retired an instruction and the
183
+ // update wasn't suppressed by writing to it explicitly or
184
+ // mcountinhibit[IR] or minstretcfg.
185
+ if retired & minstret_increment then minstret = minstret + 1;
186
+
216
187
/* for step extensions */
217
188
ext_post_step_hook();
189
+ // Return that we have stepped and are active.
190
+ true
218
191
}
219
- };
220
-
221
- stepped
192
+ }
222
193
}
223
194
224
195
function loop () : unit -> unit = {
225
196
let insns_per_tick = plat_insns_per_tick();
226
- var i : int = 0;
227
- var step_no : int = 0;
197
+ var i : nat = 0;
198
+ var step_no : nat = 0;
228
199
while not(htif_done) do {
229
200
// This standalone loop always exits immediately out of waiting
230
201
// states.
0 commit comments