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
|
/* The emulator fetch-execute-interrupt dispatch loop. */
union FetchResult = {
F_Base : word, /* Base ISA */
F_RVC : half, /* Compressed ISA */
F_Error : (ExceptionType, xlenbits) /* exception and PC */
}
function isRVC(h : half) -> bool =
~ (h[1 .. 0] == 0b11)
val fetch : unit -> FetchResult effect {escape, rmem, rreg, wmv, wreg}
function fetch() -> FetchResult =
/* check for legal PC */
if (PC[0] != 0b0 | (PC[1] != 0b0 & (~ (haveRVC()))))
then F_Error(E_Fetch_Addr_Align, PC)
else match translateAddr(PC, Execute, Instruction) {
TR_Failure(e) => F_Error(e, PC),
TR_Address(ppclo) => {
/* split instruction fetch into 16-bit granules to handle RVC, as
* well as to generate precise fault addresses in any fetch
* exceptions.
*/
match checked_mem_read(Instruction, ppclo, 2, false, false, false) {
MemException(e) => F_Error(E_Fetch_Access_Fault, PC),
MemValue(ilo) => {
if isRVC(ilo) then F_RVC(ilo)
else {
PChi : xlenbits = PC + 2;
match translateAddr(PChi, Execute, Instruction) {
TR_Failure(e) => F_Error(e, PChi),
TR_Address(ppchi) => {
match checked_mem_read(Instruction, ppchi, 2, false, false, false) {
MemException(e) => F_Error(E_Fetch_Access_Fault, PChi),
MemValue(ihi) => F_Base(append(ihi, ilo))
}
}
}
}
}
}
}
}
/* returns whether to increment the step count in the trace */
val step : int -> bool effect {barr, eamem, escape, exmem, rmem, rreg, wmv, wreg}
function step(step_no) = {
minstret_written = false; /* see note for minstret */
let (retired, stepped) : (bool, bool) =
match dispatchInterrupt(cur_privilege) {
Some(intr, priv) => {
print_bits("Handling interrupt: ", intr);
handle_interrupt(intr, priv);
(false, false)
},
None() => {
match fetch() {
F_Error(e, addr) => {
handle_mem_exception(addr, e);
(false, false)
},
F_RVC(h) => {
match decodeCompressed(h) {
None() => {
print_instr("[" ^ string_of_int(step_no) ^ "] [" ^ cur_privilege ^ "]: " ^ BitStr(PC) ^ " (" ^ BitStr(h) ^ ") <no-decode>");
instbits = EXTZ(h);
handle_illegal();
(false, true)
},
Some(ast) => {
print_instr("[" ^ string_of_int(step_no) ^ "] [" ^ cur_privilege ^ "]: " ^ BitStr(PC) ^ " (" ^ BitStr(h) ^ ") " ^ ast);
/* check for RVC once here instead of every RVC execute clause. */
if haveRVC() then {
nextPC = PC + 2;
(execute(ast), true)
} else {
(false, true)
}
}
}
},
F_Base(w) => {
match decode(w) {
None() => {
print_instr("[" ^ string_of_int(step_no) ^ "] [" ^ cur_privilege ^ "]: " ^ BitStr(PC) ^ " (" ^ BitStr(w) ^ ") <no-decode>");
instbits = EXTZ(w);
handle_illegal();
(false, true)
},
Some(ast) => {
print_instr("[" ^ string_of_int(step_no) ^ "] [" ^ cur_privilege ^ "]: " ^ BitStr(PC) ^ " (" ^ BitStr(w) ^ ") " ^ ast);
nextPC = PC + 4;
(execute(ast), true)
}
}
}
}
}
};
PC = nextPC;
if retired then retire_instruction();
stepped
}
val loop : unit -> unit effect {barr, eamem, escape, exmem, rmem, rreg, wmv, wreg}
function loop () = {
let insns_per_tick = plat_insns_per_tick();
i : int = 0;
step_no : int = 0;
while (~ (htif_done)) do {
let stepped = step(step_no);
if stepped then step_no = step_no + 1;
/* check htif exit */
if htif_done then {
let exit_val = unsigned(htif_exit_code);
if exit_val == 0 then print("SUCCESS")
else print_int("FAILURE: ", exit_val);
} else {
/* update time */
i = i + 1;
if i == insns_per_tick then {
tick_clock();
/* for now, we drive the platform i/o at every clock tick. */
tick_platform();
i = 0;
}
}
}
}
/* initialize model state */
function init_model () -> unit = {
init_platform (); /* devices */
init_sys (); /* processor */
init_vmem () /* virtual memory */
}
|