aboutsummaryrefslogtreecommitdiff
path: root/riscv/devices.cc
diff options
context:
space:
mode:
Diffstat (limited to 'riscv/devices.cc')
-rw-r--r--riscv/devices.cc125
1 files changed, 93 insertions, 32 deletions
diff --git a/riscv/devices.cc b/riscv/devices.cc
index 2c06f78..b816ca1 100644
--- a/riscv/devices.cc
+++ b/riscv/devices.cc
@@ -8,53 +8,92 @@ mmio_device_map_t& mmio_device_map()
return device_map;
}
+static auto empty_device = rom_device_t(std::vector<char>());
+
+bus_t::bus_t()
+ : bus_t(&empty_device)
+{
+}
+
+bus_t::bus_t(abstract_device_t* fallback)
+ : fallback(fallback)
+{
+}
+
void bus_t::add_device(reg_t addr, abstract_device_t* dev)
{
- // Searching devices via lower_bound/upper_bound
- // implicitly relies on the underlying std::map
- // container to sort the keys and provide ordered
- // iteration over this sort, which it does. (python's
- // SortedDict is a good analogy)
+ // Allow empty devices by omitting them
+ auto size = dev->size();
+ if (size == 0)
+ return;
+
+ // Reject devices that overflow address size
+ if (addr + size - 1 < addr) {
+ fprintf(stderr, "device at [%" PRIx64 ", %" PRIx64 ") overflows address size\n",
+ addr, addr + size);
+ abort();
+ }
+
+ // Reject devices that overlap other devices
+ if (auto it = devices.upper_bound(addr);
+ (it != devices.end() && addr + size - 1 >= it->first) ||
+ (it != devices.begin() && (it--, it->first + it->second->size() - 1 >= addr))) {
+ fprintf(stderr, "devices at [%" PRIx64 ", %" PRIx64 ") and [%" PRIx64 ", %" PRIx64 ") overlap\n",
+ it->first, it->first + it->second->size(), addr, addr + size);
+ abort();
+ }
+
devices[addr] = dev;
}
bool bus_t::load(reg_t addr, size_t len, uint8_t* bytes)
{
- // Find the device with the base address closest to but
- // less than addr (price-is-right search)
- auto it = devices.upper_bound(addr);
- if (devices.empty() || it == devices.begin()) {
- // Either the bus is empty, or there weren't
- // any items with a base address <= addr
- return false;
- }
- // Found at least one item with base address <= addr
- // The iterator points to the device after this, so
- // go back by one item.
- it--;
- return it->second->load(addr - it->first, len, bytes);
+ if (auto [base, dev] = find_device(addr, len); dev)
+ return dev->load(addr - base, len, bytes);
+ return false;
}
bool bus_t::store(reg_t addr, size_t len, const uint8_t* bytes)
{
- // See comments in bus_t::load
- auto it = devices.upper_bound(addr);
- if (devices.empty() || it == devices.begin()) {
- return false;
- }
- it--;
- return it->second->store(addr - it->first, len, bytes);
+ if (auto [base, dev] = find_device(addr, len); dev)
+ return dev->store(addr - base, len, bytes);
+ return false;
}
-std::pair<reg_t, abstract_device_t*> bus_t::find_device(reg_t addr)
+reg_t bus_t::size()
{
- // See comments in bus_t::load
- auto it = devices.upper_bound(addr);
- if (devices.empty() || it == devices.begin()) {
- return std::make_pair((reg_t)0, (abstract_device_t*)NULL);
+ if (auto last = devices.rbegin(); last != devices.rend())
+ return last->first + last->second->size();
+ return 0;
+}
+
+std::pair<reg_t, abstract_device_t*> bus_t::find_device(reg_t addr, size_t len)
+{
+ if (unlikely(!len || addr + len - 1 < addr))
+ return std::make_pair(0, nullptr);
+
+ // Obtain iterator to device immediately after the one that might match
+ auto it_after = devices.upper_bound(addr);
+ reg_t base, size;
+ if (likely(it_after != devices.begin())) {
+ // Obtain iterator to device that might match
+ auto it = std::prev(it_after);
+ base = it->first;
+ size = it->second->size();
+ if (likely(addr - base + len - 1 < size)) {
+ // it fully contains [addr, addr + len)
+ return std::make_pair(it->first, it->second);
+ }
}
- it--;
- return std::make_pair(it->first, it->second);
+
+ if (unlikely((it_after != devices.end() && addr + len - 1 >= it_after->first)
+ || (it_after != devices.begin() && addr - base < size))) {
+ // it_after or it contains part of, but not all of, [addr, add + len)
+ return std::make_pair(0, nullptr);
+ }
+
+ // No matching device
+ return std::make_pair(0, fallback);
}
mem_t::mem_t(reg_t size)
@@ -116,3 +155,25 @@ void mem_t::dump(std::ostream& o) {
}
}
}
+
+external_sim_device_t::external_sim_device_t(abstract_sim_if_t* sim)
+ : external_simulator(sim) {}
+
+void external_sim_device_t::set_simulator(abstract_sim_if_t* sim) {
+ external_simulator = sim;
+}
+
+bool external_sim_device_t::load(reg_t addr, size_t len, uint8_t* bytes) {
+ if (unlikely(external_simulator == nullptr)) return false;
+ return external_simulator->load(addr, len, bytes);
+}
+
+bool external_sim_device_t::store(reg_t addr, size_t len, const uint8_t* bytes) {
+ if (unlikely(external_simulator == nullptr)) return false;
+ return external_simulator->store(addr, len, bytes);
+}
+
+reg_t external_sim_device_t::size() {
+ if (unlikely(external_simulator == nullptr)) return 0;
+ return PGSIZE; // TODO: proper size
+}