forked from sysprog21/semu
-
Notifications
You must be signed in to change notification settings - Fork 0
/
plic.c
116 lines (108 loc) · 2.9 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
#include "device.h"
#include "riscv.h"
#include "riscv_private.h"
/* Make PLIC as simple as possible: 32 interrupts, no priority */
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;
}
}