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
|
#include <sys/time.h>
#include "devices.h"
#include "processor.h"
#include "simif.h"
clint_t::clint_t(const simif_t* sim, uint64_t freq_hz, bool real_time)
: sim(sim), freq_hz(freq_hz), real_time(real_time), mtime(0)
{
struct timeval base;
gettimeofday(&base, NULL);
real_time_ref_secs = base.tv_sec;
real_time_ref_usecs = base.tv_usec;
tick(0);
}
/* 0000 msip hart 0
* 0004 msip hart 1
* 4000 mtimecmp hart 0 lo
* 4004 mtimecmp hart 0 hi
* 4008 mtimecmp hart 1 lo
* 400c mtimecmp hart 1 hi
* bff8 mtime lo
* bffc mtime hi
*/
#define MSIP_BASE 0x0
#define MTIMECMP_BASE 0x4000
#define MTIME_BASE 0xbff8
bool clint_t::load(reg_t addr, size_t len, uint8_t* bytes)
{
if (len > 8)
return false;
tick(0);
if (addr >= MSIP_BASE && addr < MTIMECMP_BASE) {
if (len == 8) {
// Implement double-word loads as a pair of word loads
return load(addr, 4, bytes) && load(addr + 4, 4, bytes + 4);
}
const auto hart_id = (addr - MSIP_BASE) / sizeof(msip_t);
const msip_t res = sim->get_harts().count(hart_id) && (sim->get_harts().at(hart_id)->state.mip->read() & MIP_MSIP);
read_little_endian_reg(res, addr, len, bytes);
return true;
} else if (addr >= MTIMECMP_BASE && addr < MTIME_BASE) {
const auto hart_id = (addr - MTIMECMP_BASE) / sizeof(mtimecmp_t);
const mtime_t res = sim->get_harts().count(hart_id) ? mtimecmp[hart_id] : 0;
read_little_endian_reg(res, addr, len, bytes);
} else if (addr >= MTIME_BASE && addr < MTIME_BASE + sizeof(mtime_t)) {
read_little_endian_reg(mtime, addr, len, bytes);
} else if (addr + len <= CLINT_SIZE) {
memset(bytes, 0, len);
} else {
return false;
}
return true;
}
bool clint_t::store(reg_t addr, size_t len, const uint8_t* bytes)
{
if (len > 8)
return false;
if (addr >= MSIP_BASE && addr < MTIMECMP_BASE) {
if (len == 8) {
// Implement double-word stores as a pair of word stores
return store(addr, 4, bytes) && store(addr + 4, 4, bytes + 4);
}
if (addr % sizeof(msip_t) == 0) { // ignore in-between bytes
msip_t msip = 0;
write_little_endian_reg(&msip, addr, len, bytes);
const auto hart_id = (addr - MSIP_BASE) / sizeof(msip_t);
if (sim->get_harts().count(hart_id))
sim->get_harts().at(hart_id)->state.mip->backdoor_write_with_mask(MIP_MSIP, msip & 1 ? MIP_MSIP : 0);
}
} else if (addr >= MTIMECMP_BASE && addr < MTIME_BASE) {
const auto hart_id = (addr - MTIMECMP_BASE) / sizeof(mtimecmp_t);
if (sim->get_harts().count(hart_id))
write_little_endian_reg(&mtimecmp[hart_id], addr, len, bytes);
} else if (addr >= MTIME_BASE && addr < MTIME_BASE + sizeof(mtime_t)) {
write_little_endian_reg(&mtime, addr, len, bytes);
} else if (addr + len <= CLINT_SIZE) {
// Do nothing
} else {
return false;
}
tick(0);
return true;
}
void clint_t::tick(reg_t rtc_ticks)
{
if (real_time) {
struct timeval now;
uint64_t diff_usecs;
gettimeofday(&now, NULL);
diff_usecs = ((now.tv_sec - real_time_ref_secs) * 1000000) + (now.tv_usec - real_time_ref_usecs);
mtime = diff_usecs * freq_hz / 1000000;
} else {
mtime += rtc_ticks;
}
for (const auto& [hart_id, hart] : sim->get_harts()) {
hart->state.time->sync(mtime);
hart->state.mip->backdoor_write_with_mask(MIP_MTIP, mtime >= mtimecmp[hart_id] ? MIP_MTIP : 0);
}
}
|