Skip to content

Commit 292ebf4

Browse files
committed
Refactor try_step() some more
* Simplify the `stepped` code. It is equivalent to whether or not the model ends in an active state, so we can just return that. * Simplify handling of `minstret_increment`. * Change `step_no` to `nat`. * Remove unused `exit_wait` parameter from `run_hart_active()`. * Change `instbits` to 32-bit instead of xlenbits.
1 parent e4975fc commit 292ebf4

File tree

5 files changed

+90
-119
lines changed

5 files changed

+90
-119
lines changed

model/riscv_platform.sail

+3-3
Original file line numberDiff line numberDiff line change
@@ -446,9 +446,9 @@ function tick_platform() -> unit = {
446446

447447
/* Platform-specific handling of instruction faults */
448448

449-
function handle_illegal(instbits: xlenbits) -> unit = {
450-
let info = if plat_mtval_has_illegal_inst_bits ()
451-
then Some(instbits)
449+
function handle_illegal(instbits : instbits) -> unit = {
450+
let info = if plat_mtval_has_illegal_inst_bits()
451+
then Some(zero_extend(xlen, instbits))
452452
else None();
453453
let t : sync_exception = struct { trap = E_Illegal_Instr(),
454454
excinfo = info,

model/riscv_step.sail

+68-97
Original file line numberDiff line numberDiff line change
@@ -10,28 +10,6 @@
1010

1111
register hart_state : HartState = HART_ACTIVE()
1212

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-
3513
union Step = {
3614
Step_Pending_Interrupt : (InterruptType, Privilege),
3715
Step_Ext_Fetch_Failure : ext_fetch_addr_error,
@@ -40,15 +18,15 @@ union Step = {
4018
Step_Waiting : unit,
4119
}
4220

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 = {
4422
if shouldWakeForInterrupt() then {
4523
if get_config_print_instr()
4624
then print_instr("interrupt exit from WAIT state at PC " ^ BitStr(PC));
4725

4826
hart_state = HART_ACTIVE();
4927
// The waiting instruction retires successfully. The
5028
// pending interrupts will be handled in the next step.
51-
(Step_Execute(RETIRE_OK(), instbits), true)
29+
Step_Execute(RETIRE_OK(), instbits)
5230
} else if exit_wait then {
5331
// There are no pending interrupts; transition out of the Wait
5432
// as instructed.
@@ -61,27 +39,27 @@ function run_hart_waiting(step_no : int, exit_wait : bool, instbits : instbits)
6139
// implementation-specific, bounded time limit, the WFI
6240
// instruction causes an illegal-instruction exception."
6341
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)
6644
} else {
6745
if get_config_print_instr()
6846
then print_instr("remaining in WAIT state at PC " ^ BitStr(PC));
69-
(Step_Waiting(), false)
47+
Step_Waiting()
7048
}
7149
}
7250

73-
function run_hart_active(step_no: int, exit_wait : bool) -> (Step, bool) = {
51+
function run_hart_active(step_no: nat) -> Step = {
7452
match dispatchInterrupt(cur_privilege) {
75-
Some(intr, priv) => (Step_Pending_Interrupt(intr, priv), false),
53+
Some(intr, priv) => Step_Pending_Interrupt(intr, priv),
7654
None() => match ext_fetch_hook(fetch()) {
7755
/* extension error */
78-
F_Ext_Error(e) => (Step_Ext_Fetch_Failure(e), false),
56+
F_Ext_Error(e) => Step_Ext_Fetch_Failure(e),
7957
/* 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),
8159
/* non-error cases: */
8260
F_RVC(h) => {
8361
sail_instr_announce(h);
84-
let instbits : xlenbits = zero_extend(h);
62+
let instbits : instbits = zero_extend(h);
8563
let ast = ext_decode_compressed(h);
8664
if get_config_print_instr()
8765
then {
@@ -91,9 +69,9 @@ function run_hart_active(step_no: int, exit_wait : bool) -> (Step, bool) = {
9169
if currentlyEnabled(Ext_Zca) then {
9270
nextPC = PC + 2;
9371
let r = execute(ast);
94-
(Step_Execute(r, instbits), retires_or_traps(r))
72+
Step_Execute(r, instbits)
9573
} else {
96-
(Step_Execute(RETIRE_FAIL(Illegal_Instruction()), instbits), true)
74+
Step_Execute(RETIRE_FAIL(Illegal_Instruction()), instbits)
9775
}
9876
},
9977
F_Base(w) => {
@@ -106,7 +84,7 @@ function run_hart_active(step_no: int, exit_wait : bool) -> (Step, bool) = {
10684
};
10785
nextPC = PC + 4;
10886
let r = execute(ast);
109-
(Step_Execute(r, instbits), retires_or_traps(r))
87+
Step_Execute(r, instbits)
11088
}
11189
}
11290
}
@@ -117,12 +95,28 @@ function wfi_is_nop() -> bool = config platform.wfi_is_nop
11795
// The `try_step` function is the main internal driver of the Sail
11896
// model. It performs the fetch-decode-execute for an instruction. It
11997
// 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 = {
126120
/* for step extensions */
127121
ext_pre_step_hook();
128122

@@ -136,95 +130,72 @@ function try_step(step_no : int, exit_wait : bool) -> bool = {
136130
*/
137131
minstret_increment = should_inc_minstret(cur_privilege);
138132

139-
let (step_val, did_step) : (Step, bool) = match hart_state {
133+
let step_val : Step = match hart_state {
140134
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),
142136
};
143137

144-
var stepped : bool = did_step;
145-
146138
match step_val {
147139
Step_Pending_Interrupt(intr, priv) => {
148140
if get_config_print_instr()
149141
then print_bits("Handling interrupt: ", interruptType_to_bits(intr));
150-
minstret_increment = false;
151142
handle_interrupt(intr, priv)
152143
},
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)),
167148
// 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),
180152
Step_Execute(RETIRE_FAIL(Wait_For_Interrupt()), instbits) =>
181153
if wfi_is_nop() then {
182154
// This is the same as the RETIRE_OK case.
183155
assert(hart_is_active(hart_state));
184-
// Override the default step for WFI.
185-
stepped = true;
186156
} else {
187157
// Transition into the wait state.
188158
if get_config_print_instr()
189159
then print_instr("entering WAIT state at PC " ^ BitStr(PC));
190160
hart_state = HART_WAITING(instbits);
191161
},
192162
// 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(),
209167
};
210168

211169
match hart_state {
212-
HART_WAITING(_) => (),
170+
HART_WAITING(_) => false,
213171
HART_ACTIVE() => {
214172
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+
216187
/* for step extensions */
217188
ext_post_step_hook();
189+
// Return that we have stepped and are active.
190+
true
218191
}
219-
};
220-
221-
stepped
192+
}
222193
}
223194

224195
function loop () : unit -> unit = {
225196
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;
228199
while not(htif_done) do {
229200
// This standalone loop always exits immediately out of waiting
230201
// states.

model/riscv_step_common.sail

+16-15
Original file line numberDiff line numberDiff line change
@@ -18,23 +18,13 @@
1818
// instruction should finish execution. Note that in the latter
1919
// case, the platform can decide to end the stall for any reason,
2020
// even if there are no interrupts or invalidated reservation set.
21-
22-
type instbits = xlenbits
23-
2421
union HartState = {
2522
HART_ACTIVE : unit,
26-
HART_WAITING : instbits, // the instruction that caused the wait
27-
}
28-
29-
/* The result of a fetch, which includes any possible error
30-
* from an extension that interposes on the fetch operation.
31-
*/
32-
33-
union FetchResult = {
34-
F_Ext_Error : ext_fetch_addr_error, /* For extensions */
35-
F_Base : word, /* Base ISA */
36-
F_RVC : half, /* Compressed ISA */
37-
F_Error : (ExceptionType, xlenbits) /* standard exception and PC */
23+
// The instruction that caused the wait. This is needed
24+
// because WFI and WRS can time out and cause an illegal
25+
// instruction exception so we need to know what to write
26+
// to mtval.
27+
HART_WAITING : instbits,
3828
}
3929

4030
// utility predicates
@@ -50,3 +40,14 @@ function hart_is_waiting(s : HartState) -> bool =
5040
HART_ACTIVE() => false,
5141
HART_WAITING(_) => true,
5242
}
43+
44+
/* The result of a fetch, which includes any possible error
45+
* from an extension that interposes on the fetch operation.
46+
*/
47+
48+
union FetchResult = {
49+
F_Ext_Error : ext_fetch_addr_error, /* For extensions */
50+
F_Base : word, /* Base ISA */
51+
F_RVC : half, /* Compressed ISA */
52+
F_Error : (ExceptionType, xlenbits) /* standard exception and PC */
53+
}

model/riscv_sys_regs.sail

-4
Original file line numberDiff line numberDiff line change
@@ -638,10 +638,6 @@ register minstret : bits(64)
638638
/* Should minstret be incremented when the instruction is retired. */
639639
register minstret_increment : bool
640640

641-
function update_minstret() -> unit = {
642-
if minstret_increment then minstret = minstret + 1;
643-
}
644-
645641
/* machine information registers */
646642
register mvendorid : bits(32) = zeros()
647643
register mimpid : xlenbits = zeros()

model/riscv_types.sail

+3
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ let xlen_min_signed = 0 - 2 ^ (xlen - 1)
2020
type half = bits(16)
2121
type word = bits(32)
2222

23+
// Bit-vector of an uncompressed instruction.
24+
type instbits = bits(32)
25+
2326
type pagesize_bits : Int = 12
2427
let pagesize_bits = sizeof(pagesize_bits)
2528

0 commit comments

Comments
 (0)