201 lines
7.1 KiB
C++
201 lines
7.1 KiB
C++
#include "tb.h"
|
|
|
|
#include <fstream>
|
|
#include <iostream>
|
|
|
|
mem_io_state::mem_io_state(const tb_cli_args &args) : uart(args) {
|
|
mtime = 0;
|
|
mtimecmp[0] = 0;
|
|
mtimecmp[1] = 0;
|
|
exit_req = false;
|
|
exit_code = 0;
|
|
monitor_enabled = false;
|
|
soft_irq_state = 0;
|
|
irq_state = 0;
|
|
for (int i = 0; i < N_RESERVATIONS; ++i) {
|
|
reservation_valid[i] = false;
|
|
reservation_addr[i] = 0;
|
|
}
|
|
poison_addr = -4u;
|
|
mem = new uint8_t[MEM_SIZE];
|
|
for (size_t i = 0; i < MEM_SIZE; ++i)
|
|
mem[i] = 0;
|
|
|
|
if (args.load_bin) {
|
|
std::ifstream fd(args.bin_path, std::ios::binary | std::ios::ate);
|
|
if (!fd){
|
|
std::cerr << "Failed to open \"" << args.bin_path << "\"\n";
|
|
exit(-1);
|
|
}
|
|
std::streamsize bin_size = fd.tellg();
|
|
if (bin_size > MEM_SIZE) {
|
|
std::cerr << "Binary file (" << bin_size << " bytes) is larger than memory (" << MEM_SIZE << " bytes)\n";
|
|
exit(-1);
|
|
}
|
|
fd.seekg(0, std::ios::beg);
|
|
fd.read((char*)mem, bin_size);
|
|
}
|
|
}
|
|
|
|
bus_response tb_mem_access(tb_top &tb, mem_io_state &memio, bus_request req) {
|
|
bus_response resp;
|
|
|
|
// Global monitor. When monitor is not enabled, HEXOKAY is tied high
|
|
if (memio.monitor_enabled) {
|
|
if (req.excl) {
|
|
// Always set reservation on read. Always clear reservation on
|
|
// write. On successful write, clear others' matching reservations.
|
|
if (req.write) {
|
|
resp.exokay = memio.reservation_valid[req.reservation_id] &&
|
|
memio.reservation_addr[req.reservation_id] == (req.addr & RESERVATION_ADDR_MASK);
|
|
memio.reservation_valid[req.reservation_id] = false;
|
|
if (resp.exokay) {
|
|
for (int i = 0; i < N_RESERVATIONS; ++i) {
|
|
if (i == req.reservation_id)
|
|
continue;
|
|
if (memio.reservation_addr[i] == (req.addr & RESERVATION_ADDR_MASK))
|
|
memio.reservation_valid[i] = false;
|
|
}
|
|
}
|
|
} else {
|
|
resp.exokay = true;
|
|
memio.reservation_valid[req.reservation_id] = true;
|
|
memio.reservation_addr[req.reservation_id] = req.addr & RESERVATION_ADDR_MASK;
|
|
}
|
|
} else {
|
|
resp.exokay = false;
|
|
// Non-exclusive write still clears others' reservations
|
|
if (req.write) {
|
|
for (int i = 0; i < N_RESERVATIONS; ++i) {
|
|
if (i == req.reservation_id)
|
|
continue;
|
|
if (memio.reservation_addr[i] == (req.addr & RESERVATION_ADDR_MASK))
|
|
memio.reservation_valid[i] = false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
if (req.write) {
|
|
if (memio.monitor_enabled && req.excl && !resp.exokay) {
|
|
// Failed exclusive write; do nothing
|
|
} else if ((req.addr & -4u) == memio.poison_addr) {
|
|
resp.err = true;
|
|
} else if (req.addr >= MEM_BASE && req.addr <= MEM_BASE + MEM_SIZE - (1u << (int)req.size)) {
|
|
unsigned int n_bytes = 1u << (int)req.size;
|
|
// Note we are relying on hazard3's byte lane replication
|
|
for (unsigned int i = 0; i < n_bytes; ++i) {
|
|
memio.mem[req.addr + i - MEM_BASE] = req.wdata >> (8 * i) & 0xffu;
|
|
}
|
|
} else if (req.addr == IO_BASE + IO_PRINT_CHAR) {
|
|
const uint8_t ch = (uint8_t)(req.wdata & 0xffu);
|
|
fprintf(tb.logfile, "%c", (char)ch);
|
|
memio.uart.write_data(0, ch);
|
|
} else if (req.addr == IO_BASE + IO_PRINT_U32) {
|
|
fprintf(tb.logfile, "%08x\n", req.wdata);
|
|
} else if (req.addr == IO_BASE + IO_EXIT) {
|
|
if (!memio.exit_req) {
|
|
memio.exit_req = true;
|
|
memio.exit_code = req.wdata;
|
|
}
|
|
} else if (req.addr == IO_BASE + IO_SET_SOFTIRQ) {
|
|
memio.soft_irq_state |= req.wdata;
|
|
tb.set_soft_irq(memio.soft_irq_state);
|
|
} else if (req.addr == IO_BASE + IO_CLR_SOFTIRQ) {
|
|
memio.soft_irq_state &= ~req.wdata;
|
|
tb.set_soft_irq(memio.soft_irq_state);
|
|
} else if (req.addr == IO_BASE + IO_GLOBMON_EN) {
|
|
memio.monitor_enabled = req.wdata;
|
|
} else if (req.addr == IO_BASE + IO_POISON_ADDR) {
|
|
memio.poison_addr = req.wdata & -4u;
|
|
} else if (req.addr == IO_BASE + IO_SET_IRQ) {
|
|
memio.irq_state |= req.wdata;
|
|
tb.set_irq(memio.irq_state);
|
|
} else if (req.addr == IO_BASE + IO_CLR_IRQ) {
|
|
memio.irq_state &= ~req.wdata;
|
|
tb.set_irq(memio.irq_state);
|
|
} else if (req.addr == IO_BASE + IO_MTIME) {
|
|
memio.mtime = (memio.mtime & 0xffffffff00000000u) | req.wdata;
|
|
} else if (req.addr == IO_BASE + IO_MTIMEH) {
|
|
memio.mtime = (memio.mtime & 0x00000000ffffffffu) | ((uint64_t)req.wdata << 32);
|
|
} else if (req.addr == IO_BASE + IO_MTIMECMP0) {
|
|
memio.mtimecmp[0] = (memio.mtimecmp[0] & 0xffffffff00000000u) | req.wdata;
|
|
} else if (req.addr == IO_BASE + IO_MTIMECMP0H) {
|
|
memio.mtimecmp[0] = (memio.mtimecmp[0] & 0x00000000ffffffffu) | ((uint64_t)req.wdata << 32);
|
|
} else if (req.addr == IO_BASE + IO_MTIMECMP1) {
|
|
memio.mtimecmp[1] = (memio.mtimecmp[1] & 0xffffffff00000000u) | req.wdata;
|
|
} else if (req.addr == IO_BASE + IO_MTIMECMP1H) {
|
|
memio.mtimecmp[1] = (memio.mtimecmp[1] & 0x00000000ffffffffu) | ((uint64_t)req.wdata << 32);
|
|
} else if (req.addr >= IO_BASE + IO_UART_BASE && req.addr < IO_BASE + IO_UART_BASE + IO_UART_N * IO_UART_STRIDE) {
|
|
const uint32_t rel = req.addr - (IO_BASE + IO_UART_BASE);
|
|
const uint32_t uart_idx = rel / IO_UART_STRIDE;
|
|
const uint32_t reg_off = rel % IO_UART_STRIDE;
|
|
if (reg_off == IO_UART_DATA) {
|
|
const uint8_t ch = (uint8_t)(req.wdata & 0xffu);
|
|
if (uart_idx == 0)
|
|
fprintf(tb.logfile, "%c", (char)ch);
|
|
memio.uart.write_data(uart_idx, ch);
|
|
} else if (reg_off == IO_UART_CTRL) {
|
|
memio.uart.write_ctrl(uart_idx, req.wdata);
|
|
} else {
|
|
resp.err = true;
|
|
}
|
|
} else {
|
|
resp.err = true;
|
|
}
|
|
} else {
|
|
if (req.addr == (memio.poison_addr & -4u)) {
|
|
resp.err = true;
|
|
} else if (req.addr >= MEM_BASE && req.addr <= MEM_BASE + MEM_SIZE - (1u << (int)req.size)) {
|
|
req.addr &= ~0x3u;
|
|
req.addr -= MEM_BASE;
|
|
resp.rdata =
|
|
(uint32_t)memio.mem[req.addr] |
|
|
memio.mem[req.addr + 1] << 8 |
|
|
memio.mem[req.addr + 2] << 16 |
|
|
memio.mem[req.addr + 3] << 24;
|
|
} else if (req.addr >= IO_BASE + IO_UART_BASE && req.addr < IO_BASE + IO_UART_BASE + IO_UART_N * IO_UART_STRIDE) {
|
|
const uint32_t rel = req.addr - (IO_BASE + IO_UART_BASE);
|
|
const uint32_t uart_idx = rel / IO_UART_STRIDE;
|
|
const uint32_t reg_off = rel % IO_UART_STRIDE;
|
|
if (reg_off == IO_UART_STATUS) {
|
|
resp.rdata = memio.uart.read_status(uart_idx);
|
|
} else if (reg_off == IO_UART_DATA) {
|
|
resp.rdata = memio.uart.read_data(uart_idx);
|
|
} else {
|
|
resp.err = true;
|
|
}
|
|
} else if (req.addr == IO_BASE + IO_SET_SOFTIRQ || req.addr == IO_BASE + IO_CLR_SOFTIRQ) {
|
|
resp.rdata = memio.soft_irq_state;
|
|
} else if (req.addr == IO_BASE + IO_SET_IRQ || req.addr == IO_BASE + IO_CLR_IRQ) {
|
|
resp.rdata = memio.irq_state;
|
|
} else if (req.addr == IO_BASE + IO_MTIME) {
|
|
resp.rdata = memio.mtime;
|
|
} else if (req.addr == IO_BASE + IO_MTIMEH) {
|
|
resp.rdata = memio.mtime >> 32;
|
|
} else if (req.addr == IO_BASE + IO_MTIMECMP0) {
|
|
resp.rdata = memio.mtimecmp[0];
|
|
} else if (req.addr == IO_BASE + IO_MTIMECMP0H) {
|
|
resp.rdata = memio.mtimecmp[0] >> 32;
|
|
} else if (req.addr == IO_BASE + IO_MTIMECMP1) {
|
|
resp.rdata = memio.mtimecmp[1];
|
|
} else if (req.addr == IO_BASE + IO_MTIMECMP1H) {
|
|
resp.rdata = memio.mtimecmp[1] >> 32;
|
|
} else {
|
|
resp.err = true;
|
|
}
|
|
}
|
|
if (resp.err) {
|
|
resp.exokay = false;
|
|
}
|
|
return resp;
|
|
}
|
|
|
|
void mem_io_state::step(tb_top &tb) {
|
|
// Default update logic for mtime, mtimecmp
|
|
++mtime;
|
|
tb.set_timer_irq((uint8_t)((mtime >= mtimecmp[0]) | (mtime >= mtimecmp[1]) << 1));
|
|
uart.step();
|
|
}
|