aboutsummaryrefslogtreecommitdiff
path: root/riscv/clint.cc
blob: 908ccb606a1d32e02ebdcb492aa5d5fa984942b0 (plain)
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
138
139
140
141
142
143
144
145
#include <sys/time.h>
#include <sstream>
#include "devices.h"
#include "processor.h"
#include "simif.h"
#include "sim.h"
#include "dts.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);
  }
}

clint_t* clint_parse_from_fdt(const void* fdt, const sim_t* sim, reg_t* base) {
  if (fdt_parse_clint(fdt, base, "riscv,clint0") == 0)
    return new clint_t(sim,
                       sim->CPU_HZ / sim->INSNS_PER_RTC_TICK,
                       sim->get_cfg().real_time_clint());
  else
    return nullptr;
}

std::string clint_generate_dts(const sim_t* sim) {
  std::stringstream s;
  s << std::hex
    << "    clint@" << CLINT_BASE << " {\n"
       "      compatible = \"riscv,clint0\";\n"
       "      interrupts-extended = <" << std::dec;
  for (size_t i = 0; i < sim->get_cfg().nprocs(); i++)
    s << "&CPU" << i << "_intc 3 &CPU" << i << "_intc 7 ";
  reg_t clintbs = CLINT_BASE;
  reg_t clintsz = CLINT_SIZE;
  s << std::hex << ">;\n"
    "      reg = <0x" << (clintbs >> 32) << " 0x" << (clintbs & (uint32_t)-1) <<
    " 0x" << (clintsz >> 32) << " 0x" << (clintsz & (uint32_t)-1) << ">;\n"
    "    };\n";
  return s.str();
}

REGISTER_DEVICE(clint, clint_parse_from_fdt, clint_generate_dts)