feat: publish FreeRTOS C FC11 card
This commit is contained in:
+108
@@ -0,0 +1,108 @@
|
||||
#pragma once
|
||||
|
||||
#include "tb_cli.h"
|
||||
#include "tb_constants.h"
|
||||
#include "tb_uart.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <cstdio>
|
||||
|
||||
#include <unistd.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
|
||||
struct mem_io_state;
|
||||
class tb_top;
|
||||
|
||||
struct bus_request {
|
||||
uint32_t addr;
|
||||
bus_size_t size;
|
||||
bool write;
|
||||
bool excl;
|
||||
uint32_t wdata;
|
||||
int reservation_id;
|
||||
bus_request(): addr(0), size(SIZE_BYTE), write(0), excl(0), wdata(0), reservation_id(0) {}
|
||||
};
|
||||
|
||||
struct bus_response {
|
||||
uint32_t rdata;
|
||||
int stall_cycles;
|
||||
bool err;
|
||||
bool exokay;
|
||||
bus_response(): rdata(0), stall_cycles(0), err(false), exokay(true) {}
|
||||
};
|
||||
|
||||
typedef bus_response (*mem_access_callback_t)(tb_top &tb, mem_io_state &memio, bus_request req);
|
||||
|
||||
// Default callback:
|
||||
bus_response tb_mem_access(tb_top &tb, mem_io_state &memio, bus_request req);
|
||||
|
||||
// Abstract test harness class. Concrete implementations of this class contain
|
||||
// the actual C++ cycle model as well as the glue for this interface.
|
||||
class tb_top {
|
||||
protected:
|
||||
mem_access_callback_t mem_callback_i;
|
||||
mem_access_callback_t mem_callback_d;
|
||||
uint64_t rand_state[4];
|
||||
public:
|
||||
FILE *logfile;
|
||||
void set_mem_callback_i(mem_access_callback_t cb) {mem_callback_i = cb;}
|
||||
void set_mem_callback_d(mem_access_callback_t cb) {mem_callback_d = cb;}
|
||||
|
||||
tb_top(const tb_cli_args &args) {
|
||||
mem_callback_i = tb_mem_access;
|
||||
mem_callback_d = tb_mem_access;
|
||||
seed_rand((const uint8_t*)"looks random to me", 18);
|
||||
if (args.log_path != "") {
|
||||
logfile = fopen(args.log_path.c_str(), "wb");
|
||||
} else {
|
||||
logfile = stdout;
|
||||
}
|
||||
}
|
||||
|
||||
void seed_rand(const uint8_t *data, size_t len);
|
||||
uint32_t rand();
|
||||
|
||||
virtual void step(const tb_cli_args &args, mem_io_state &memio) = 0;
|
||||
// Evaluate DUT at current signal values without advancing the core clock.
|
||||
virtual void eval() = 0;
|
||||
|
||||
virtual void set_trst_n(bool trst_n) = 0;
|
||||
virtual void set_tck(bool tck) = 0;
|
||||
virtual void set_tdi(bool tdi) = 0;
|
||||
virtual void set_tms(bool tms) = 0;
|
||||
virtual bool get_tdo() = 0;
|
||||
|
||||
virtual void set_irq(uint32_t mask) = 0;
|
||||
virtual void set_soft_irq(uint8_t mask) = 0;
|
||||
virtual void set_timer_irq(uint8_t mask) = 0;
|
||||
};
|
||||
|
||||
struct mem_io_state {
|
||||
uint64_t mtime;
|
||||
uint64_t mtimecmp[2];
|
||||
|
||||
bool exit_req;
|
||||
uint32_t exit_code;
|
||||
|
||||
uint8_t *mem;
|
||||
|
||||
bool monitor_enabled;
|
||||
bool reservation_valid[2];
|
||||
uint32_t reservation_addr[2];
|
||||
uint32_t poison_addr;
|
||||
|
||||
uint8_t soft_irq_state;
|
||||
uint32_t irq_state;
|
||||
|
||||
tb_uart_state uart;
|
||||
|
||||
mem_io_state(const tb_cli_args &args);
|
||||
|
||||
~mem_io_state() {
|
||||
delete[] mem;
|
||||
}
|
||||
|
||||
void step(tb_top &tb);
|
||||
};
|
||||
@@ -0,0 +1,57 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
struct tb_cli_args {
|
||||
bool load_bin;
|
||||
std::string bin_path;
|
||||
bool dump_waves;
|
||||
std::string waves_path;
|
||||
std::vector<std::pair<uint32_t, uint32_t>> dump_ranges;
|
||||
int64_t max_cycles;
|
||||
bool propagate_return_code;
|
||||
uint16_t port;
|
||||
uint16_t vpi_port;
|
||||
uint16_t gdb_port;
|
||||
uint16_t uart0_port;
|
||||
uint16_t uart1_port;
|
||||
// JTAG_VPI socket polling backoff in core cycles (0 = poll every cycle).
|
||||
// When idle, the testbench ramps up to this maximum backoff to reduce
|
||||
// syscall overhead without adding huge latency between back-to-back packets.
|
||||
uint32_t vpi_poll_cycles;
|
||||
// When using --vpi-port, run N core clock cycles per JTAG TCK cycle during
|
||||
// OpenOCD TMS sequences (helps DMI/APB CDC make progress and avoids BUSY).
|
||||
// 0 disables this coupling (legacy behaviour).
|
||||
uint32_t vpi_clk_per_tck;
|
||||
bool dump_jtag;
|
||||
std::string jtag_dump_path;
|
||||
bool replay_jtag;
|
||||
std::string jtag_replay_path;
|
||||
std::string log_path;
|
||||
std::string sig_path;
|
||||
#ifdef CXXRTL_DEBUG_AGENT
|
||||
bool run_agent;
|
||||
#endif
|
||||
tb_cli_args() {
|
||||
load_bin = false;
|
||||
dump_waves = false;
|
||||
max_cycles = 0;
|
||||
propagate_return_code = false;
|
||||
port = 0;
|
||||
vpi_port = 0;
|
||||
gdb_port = 0;
|
||||
uart0_port = 0;
|
||||
uart1_port = 0;
|
||||
vpi_poll_cycles = 256;
|
||||
vpi_clk_per_tck = 2;
|
||||
dump_jtag = false;
|
||||
replay_jtag = false;
|
||||
#ifdef CXXRTL_DEBUG_AGENT
|
||||
run_agent = false;
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
void tb_parse_args(int argc, char **argv, tb_cli_args &args);
|
||||
@@ -0,0 +1,57 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef __x86_64__
|
||||
#define I64_FMT "%ld"
|
||||
#else
|
||||
#define I64_FMT "%lld"
|
||||
#endif
|
||||
|
||||
#define MEM_BASE 0x80000000
|
||||
#define MEM_SIZE (16 * 1024 * 1024)
|
||||
#define N_RESERVATIONS (2)
|
||||
#define RESERVATION_ADDR_MASK (0xfffffff8u)
|
||||
|
||||
static const unsigned int IO_BASE = 0xc0000000;
|
||||
enum {
|
||||
IO_PRINT_CHAR = 0x000,
|
||||
IO_PRINT_U32 = 0x004,
|
||||
IO_EXIT = 0x008,
|
||||
IO_SET_SOFTIRQ = 0x010,
|
||||
IO_CLR_SOFTIRQ = 0x014,
|
||||
IO_GLOBMON_EN = 0x018,
|
||||
IO_POISON_ADDR = 0x01c,
|
||||
IO_SET_IRQ = 0x020,
|
||||
IO_CLR_IRQ = 0x030,
|
||||
IO_MTIME = 0x100,
|
||||
IO_MTIMEH = 0x104,
|
||||
IO_MTIMECMP0 = 0x108,
|
||||
IO_MTIMECMP0H = 0x10c,
|
||||
IO_MTIMECMP1 = 0x110,
|
||||
IO_MTIMECMP1H = 0x114
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Testbench UART-over-TCP MMIO
|
||||
//
|
||||
// Each UART is exposed as a raw TCP byte stream (one client at a time).
|
||||
// Software should poll STATUS.RX_AVAIL before reading DATA.
|
||||
|
||||
static constexpr uint32_t IO_UART_BASE = 0x200;
|
||||
static constexpr uint32_t IO_UART_STRIDE = 0x20;
|
||||
static constexpr uint32_t IO_UART_DATA = 0x00;
|
||||
static constexpr uint32_t IO_UART_STATUS = 0x04;
|
||||
static constexpr uint32_t IO_UART_CTRL = 0x08;
|
||||
static constexpr uint32_t IO_UART_N = 2;
|
||||
|
||||
static constexpr uint32_t TB_UART_STATUS_RX_AVAIL = 1u << 0;
|
||||
static constexpr uint32_t TB_UART_STATUS_TX_READY = 1u << 1;
|
||||
static constexpr uint32_t TB_UART_STATUS_CONNECTED = 1u << 2;
|
||||
static constexpr uint32_t TB_UART_STATUS_OVERRUN = 1u << 3;
|
||||
|
||||
static constexpr uint32_t TB_UART_CTRL_CLR_OVERRUN = 1u << 0;
|
||||
|
||||
typedef enum {
|
||||
SIZE_BYTE = 0,
|
||||
SIZE_HWORD = 1,
|
||||
SIZE_WORD = 2
|
||||
} bus_size_t;
|
||||
@@ -0,0 +1,85 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <unordered_set>
|
||||
|
||||
enum class tb_gdb_run_result {
|
||||
ok = 0,
|
||||
error,
|
||||
exited,
|
||||
timed_out
|
||||
};
|
||||
|
||||
struct tb_gdb_target {
|
||||
virtual ~tb_gdb_target() = default;
|
||||
|
||||
// Register numbering follows the provided target.xml:
|
||||
// x0..x31 = 0..31, pc = 32.
|
||||
virtual uint32_t read_reg(uint32_t regno) = 0;
|
||||
virtual void write_reg(uint32_t regno, uint32_t value) = 0;
|
||||
|
||||
virtual bool read_mem(uint32_t addr, uint8_t *dst, size_t len) = 0;
|
||||
virtual bool write_mem(uint32_t addr, const uint8_t *src, size_t len) = 0;
|
||||
|
||||
// Advance execution until one instruction retires.
|
||||
virtual tb_gdb_run_result step_instruction() = 0;
|
||||
|
||||
virtual uint32_t exit_code() const = 0;
|
||||
|
||||
// Optional: handle GDB "monitor" commands (qRcmd). Return true if handled.
|
||||
// If handled, out_console is printed on the GDB console (can be empty).
|
||||
virtual bool monitor_cmd(const std::string &cmd, std::string &out_console) {
|
||||
(void)cmd;
|
||||
out_console.clear();
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
class tb_gdb_server {
|
||||
public:
|
||||
tb_gdb_server(uint16_t port, tb_gdb_target &target);
|
||||
~tb_gdb_server();
|
||||
|
||||
// Blocks until the session ends (disconnect/kill) or the target exits.
|
||||
tb_gdb_run_result serve();
|
||||
|
||||
private:
|
||||
uint16_t port_;
|
||||
tb_gdb_target &target_;
|
||||
|
||||
int server_fd_ = -1;
|
||||
int client_fd_ = -1;
|
||||
bool no_ack_mode_ = false;
|
||||
|
||||
std::string target_xml_;
|
||||
std::string memory_map_xml_;
|
||||
|
||||
// Execute breakpoints (RSP Z0/Z1): stop before executing instruction at PC.
|
||||
// When resuming from a breakpoint, ignore a match at the current PC once.
|
||||
std::unordered_set<uint32_t> breakpoints_;
|
||||
uint32_t ignore_breakpoint_pc_ = 0xffffffffu;
|
||||
|
||||
// Socket helpers
|
||||
bool open_listen_socket_();
|
||||
bool accept_client_();
|
||||
void close_client_();
|
||||
|
||||
bool recv_packet_(std::string &out_payload, bool &got_interrupt);
|
||||
bool send_packet_(const std::string &payload);
|
||||
bool maybe_send_ack_(bool ok);
|
||||
bool check_interrupt_();
|
||||
|
||||
// RSP helpers
|
||||
static uint8_t checksum_(const std::string &s);
|
||||
static int hex_val_(char c);
|
||||
static std::string to_hex_bytes_(const uint8_t *data, size_t len);
|
||||
static bool from_hex_bytes_(const std::string &hex, std::string &out_bytes);
|
||||
static void append_u32_le_hex_(std::string &out, uint32_t v);
|
||||
static bool parse_u32_hex_(const std::string &s, uint32_t &out);
|
||||
|
||||
std::string handle_command_(const std::string &cmd, bool &run_command_consumed);
|
||||
std::string stop_reply_(int signo) const;
|
||||
std::string stop_reply_exit_() const;
|
||||
};
|
||||
@@ -0,0 +1,58 @@
|
||||
#include <fstream>
|
||||
#include <cstdint>
|
||||
|
||||
#include <unistd.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
|
||||
#include "tb.h"
|
||||
#include "tb_cli.h"
|
||||
|
||||
#define TCP_BUF_SIZE 256
|
||||
|
||||
struct tb_jtag_state {
|
||||
enum class transport_t {
|
||||
none,
|
||||
remote_bitbang,
|
||||
jtag_vpi
|
||||
};
|
||||
|
||||
tb_cli_args args;
|
||||
transport_t transport;
|
||||
|
||||
int server_fd;
|
||||
int sock_fd;
|
||||
struct sockaddr_in sock_addr;
|
||||
int sock_opt;
|
||||
socklen_t sock_addr_len;
|
||||
char txbuf[TCP_BUF_SIZE], rxbuf[TCP_BUF_SIZE];
|
||||
int rx_ptr;
|
||||
int rx_remaining;
|
||||
int tx_ptr;
|
||||
|
||||
static constexpr int VPI_XFERT_MAX_SIZE = 512;
|
||||
static constexpr int VPI_PKT_SIZE = 4 + VPI_XFERT_MAX_SIZE + VPI_XFERT_MAX_SIZE + 4 + 4;
|
||||
uint8_t vpi_rxbuf[VPI_PKT_SIZE];
|
||||
int vpi_rx_count;
|
||||
uint32_t vpi_poll_ctr;
|
||||
uint32_t vpi_poll_backoff;
|
||||
|
||||
std::ofstream jtag_dump_fd;
|
||||
std::ifstream jtag_replay_fd;
|
||||
|
||||
tb_jtag_state(const tb_cli_args &_args);
|
||||
|
||||
// Returns true if an exit command was received from the JTAG socket
|
||||
// If memio/cycle_count/timed_out are provided, the testbench may advance
|
||||
// the core clock while processing JTAG_VPI TMS sequences (idle/wait cycles),
|
||||
// so the DMI/APB CDC can make progress and OpenOCD doesn't spin on BUSY.
|
||||
bool step(
|
||||
tb_top &tb,
|
||||
mem_io_state *memio = nullptr,
|
||||
int64_t *cycle_count = nullptr,
|
||||
bool *timed_out = nullptr,
|
||||
uint32_t *core_cycles_advanced = nullptr
|
||||
);
|
||||
|
||||
void close();
|
||||
};
|
||||
@@ -0,0 +1,54 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <deque>
|
||||
|
||||
#include <netinet/in.h>
|
||||
|
||||
struct tb_cli_args;
|
||||
|
||||
// Simple UART-over-TCP bridge used by the Verilator/CXXRTL testbenches.
|
||||
//
|
||||
// Each UART is exposed as a raw TCP byte stream (one client at a time).
|
||||
// - CPU TX: MMIO write -> queued -> non-blocking send() to connected client.
|
||||
// - CPU RX: client -> queued -> MMIO read pops one byte (poll STATUS first).
|
||||
struct tb_uart_state {
|
||||
static constexpr uint32_t N_UARTS = 2;
|
||||
|
||||
struct uart {
|
||||
uint16_t port = 0;
|
||||
int server_fd = -1;
|
||||
int client_fd = -1;
|
||||
bool overrun = false;
|
||||
|
||||
// Bounded FIFOs to avoid unbounded memory growth if the CPU or client
|
||||
// isn't keeping up. When full, RX drops new data and TX drops old data.
|
||||
std::deque<uint8_t> rx_fifo;
|
||||
std::deque<uint8_t> tx_fifo;
|
||||
size_t rx_capacity = 4096;
|
||||
size_t tx_capacity = 4096;
|
||||
|
||||
sockaddr_in bind_addr {};
|
||||
|
||||
void init(uint16_t port_);
|
||||
void close();
|
||||
void poll_accept(uint32_t uart_idx);
|
||||
void poll_rx(uint32_t uart_idx);
|
||||
void poll_tx(uint32_t uart_idx);
|
||||
bool connected() const { return client_fd >= 0; }
|
||||
};
|
||||
|
||||
uart uarts[N_UARTS];
|
||||
|
||||
tb_uart_state() = default;
|
||||
explicit tb_uart_state(const tb_cli_args &args);
|
||||
~tb_uart_state();
|
||||
|
||||
void step();
|
||||
|
||||
uint32_t read_status(uint32_t uart_idx);
|
||||
uint32_t read_data(uint32_t uart_idx);
|
||||
void write_data(uint32_t uart_idx, uint8_t byte);
|
||||
void write_ctrl(uint32_t uart_idx, uint32_t value);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user