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