forked from sysprog21/semu
-
Notifications
You must be signed in to change notification settings - Fork 5
/
plic.c
137 lines (127 loc) · 3.42 KB
/
plic.c
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
#include "device.h"
#include "riscv.h"
#include "riscv_private.h"
#include "persistence.h"
/* Make PLIC as simple as possible: 32 interrupts, no priority */
void save_plic(const vm_t *vm,
uint8_t **obufp) {
const emu_state_t *data = (const emu_state_t *) vm->priv;
const plic_state_t plic = data->plic;
SER32(plic.masked);
SER32(plic.ip);
SER32(plic.ie);
SER32(plic.active);
}
void load_plic(vm_t *vm,
uint8_t **ibufp) {
emu_state_t *data = (const emu_state_t *) vm->priv;
plic_state_t* plic = &data->plic;
DESER32(plic->masked);
DESER32(plic->ip);
DESER32(plic->ie);
DESER32(plic->active);
}
void plic_update_interrupts(vm_t *vm, plic_state_t *plic)
{
/* Update pending interrupts */
plic->ip |= plic->active & ~plic->masked;
plic->masked |= plic->active;
/* Send interrupt to target */
if (plic->ip & plic->ie)
vm->sip |= RV_INT_SEI_BIT;
else
vm->sip &= ~RV_INT_SEI_BIT;
}
static bool plic_reg_read(plic_state_t *plic, uint32_t addr, uint32_t *value)
{
/* no priority support: source priority hardwired to 1 */
if (1 <= addr && addr <= 31)
return true;
switch (addr) {
case 0x400:
*value = plic->ip;
return true;
case 0x800:
*value = plic->ie;
return true;
case 0x80000:
*value = 0;
/* no priority support: target priority threshold hardwired to 0 */
return true;
case 0x80001:
/* claim */
*value = 0;
uint32_t candidates = plic->ip & plic->ie;
if (candidates) {
*value = ilog2(candidates);
plic->ip &= ~(1 << (*value));
}
return true;
default:
return false;
}
}
static bool plic_reg_write(plic_state_t *plic, uint32_t addr, uint32_t value)
{
/* no priority support: source priority hardwired to 1 */
if (1 <= addr && addr <= 31)
return true;
switch (addr) {
case 0x800:
value &= ~1;
plic->ie = value;
return true;
case 0x80000:
/* no priority support: target priority threshold hardwired to 0 */
return true;
case 0x80001:
/* completion */
if (plic->ie & (1 << value))
plic->masked &= ~(1 << value);
return true;
default:
return false;
}
}
void plic_read(vm_t *vm,
plic_state_t *plic,
uint32_t addr,
uint8_t width,
uint32_t *value)
{
switch (width) {
case RV_MEM_LW:
if (!plic_reg_read(plic, addr >> 2, value))
vm_set_exception(vm, RV_EXC_LOAD_FAULT, vm->exc_val);
break;
case RV_MEM_LBU:
case RV_MEM_LB:
case RV_MEM_LHU:
case RV_MEM_LH:
vm_set_exception(vm, RV_EXC_LOAD_MISALIGN, vm->exc_val);
return;
default:
vm_set_exception(vm, RV_EXC_ILLEGAL_INSTR, 0);
return;
}
}
void plic_write(vm_t *vm,
plic_state_t *plic,
uint32_t addr,
uint8_t width,
uint32_t value)
{
switch (width) {
case RV_MEM_SW:
if (!plic_reg_write(plic, addr >> 2, value))
vm_set_exception(vm, RV_EXC_STORE_FAULT, vm->exc_val);
break;
case RV_MEM_SB:
case RV_MEM_SH:
vm_set_exception(vm, RV_EXC_STORE_MISALIGN, vm->exc_val);
return;
default:
vm_set_exception(vm, RV_EXC_ILLEGAL_INSTR, 0);
return;
}
}