22 changed files with 835 additions and 10 deletions
@ -0,0 +1,99 @@ |
|||
#include "led-device.h" |
|||
#include <cstring> |
|||
#include <cstdio> |
|||
#include <sstream> |
|||
#include <algorithm> |
|||
|
|||
led_device_t::led_device_t(const sim_t *sim, reg_t base, size_t size, uint32_t num_leds) |
|||
: sim(sim), base_addr(base), dev_size(size), |
|||
num_leds(std::min(num_leds, 32u)), led_state(0) { |
|||
printf("LED device init at 0x%lx, size: 0x%lx, num_leds: %u\n", |
|||
base_addr, dev_size, this->num_leds); |
|||
} |
|||
|
|||
bool led_device_t::load(reg_t addr, size_t len, uint8_t* bytes) { |
|||
printf("led_device_t::load from address: 0x%lx, length: %zu\n", |
|||
base_addr + addr, len); |
|||
|
|||
if (addr == 0 && len == 4) { |
|||
uint32_t masked_state = led_state & ((1u << num_leds) - 1); |
|||
memcpy(bytes, &masked_state, 4); |
|||
return true; |
|||
} |
|||
return false; |
|||
} |
|||
|
|||
bool led_device_t::store(reg_t addr, size_t len, const uint8_t* bytes) { |
|||
printf("led_device_t::store to address: 0x%lx, length: %zu\n", |
|||
base_addr + addr, len); |
|||
|
|||
if (addr == 0 && len == 4) { |
|||
uint32_t new_state; |
|||
memcpy(&new_state, bytes, 4); |
|||
led_state = new_state & ((1u << num_leds) - 1); |
|||
|
|||
printf("LED state changed to: 0x%x\n", led_state); |
|||
printf("LED pattern (%u LEDs): ", num_leds); |
|||
for (int i = num_leds - 1; i >= 0; i--) { |
|||
printf("%c", (led_state & (1 << i)) ? ' ' : '-'); |
|||
} |
|||
printf(" (0x%x)\n", led_state); |
|||
return true; |
|||
} |
|||
return false; |
|||
} |
|||
|
|||
size_t led_device_t::size() { |
|||
return dev_size; |
|||
} |
|||
|
|||
led_device_t* led_parse_from_fdt(const void *fdt, const sim_t *sim, |
|||
reg_t *base, std::vector<std::string> args) { |
|||
if (args.size() < 3) { |
|||
fprintf(stderr, "Error: led device requires 3 arguments (base, size, num_leds)\n"); |
|||
return nullptr; |
|||
} |
|||
|
|||
try { |
|||
*base = std::stoull(args[0], nullptr, 0); |
|||
reg_t size = std::stoull(args[1], nullptr, 0); |
|||
uint32_t num_leds = std::stoul(args[2], nullptr, 0); |
|||
|
|||
if (num_leds == 0 || num_leds > 32) { |
|||
fprintf(stderr, "Error: num_leds must be between 1 and 32, got %u\n", num_leds); |
|||
return nullptr; |
|||
} |
|||
|
|||
return new led_device_t(sim, *base, size, num_leds); |
|||
} catch (const std::exception& e) { |
|||
fprintf(stderr, "Error parsing LED device params: %s\n", e.what()); |
|||
return nullptr; |
|||
} |
|||
} |
|||
|
|||
std::string led_generate_dts(const sim_t *sim, const std::vector<std::string> &args) { |
|||
std::ostringstream s; |
|||
reg_t base = 0x10001000; |
|||
reg_t size = 0x1000; |
|||
uint32_t num_leds = 8; |
|||
|
|||
if (args.size() >= 3) { |
|||
base = std::stoull(args[0], nullptr, 0); |
|||
size = std::stoull(args[1], nullptr, 0); |
|||
num_leds = std::stoul(args[2], nullptr, 0); |
|||
} |
|||
|
|||
s << std::hex |
|||
<< " led@" << base << " {\n" |
|||
<< " compatible = \"led\";\n" |
|||
<< " reg = <0x0 0x" << base << " 0x0 0x" << size << ">;\n" |
|||
<< std::dec |
|||
<< " num-leds = <" << num_leds << ">;\n" |
|||
<< std::hex |
|||
<< " spike,plugin-params = \"0x" << base << ",0x" << size << "," << std::dec << num_leds << "\";\n" |
|||
<< " };\n"; |
|||
|
|||
return s.str(); |
|||
} |
|||
|
|||
REGISTER_DEVICE(led_device, led_parse_from_fdt, led_generate_dts) |
|||
@ -0,0 +1,26 @@ |
|||
#ifndef LED_DEVICE_H |
|||
#define LED_DEVICE_H |
|||
|
|||
#include <riscv/sim.h> |
|||
#include <vector> |
|||
#include <string> |
|||
|
|||
class led_device_t : public abstract_device_t { |
|||
public: |
|||
led_device_t(const sim_t *sim, reg_t base, size_t size, uint32_t num_leds); |
|||
|
|||
bool load(reg_t addr, size_t len, uint8_t* bytes) override; |
|||
bool store(reg_t addr, size_t len, const uint8_t* bytes) override; |
|||
size_t size() override; |
|||
|
|||
private: |
|||
const sim_t *sim; |
|||
reg_t base_addr; |
|||
size_t dev_size; |
|||
uint32_t num_leds; |
|||
uint32_t led_state; |
|||
}; |
|||
|
|||
|
|||
|
|||
#endif // LED_DEVICE_H
|
|||
@ -0,0 +1,52 @@ |
|||
/dts-v1/; |
|||
/ { |
|||
#address-cells = <2>; |
|||
#size-cells = <2>; |
|||
|
|||
cpus { |
|||
#address-cells = <1>; |
|||
#size-cells = <0>; |
|||
timebase-frequency = <10000000>; |
|||
|
|||
CPU0: cpu@0 { |
|||
device_type = "cpu"; |
|||
reg = <0>; |
|||
riscv,isa = "rv64imafdc"; |
|||
|
|||
CPU0_intc: interrupt-controller { |
|||
#interrupt-cells = <1>; |
|||
interrupt-controller; |
|||
compatible = "riscv,cpu-intc"; |
|||
}; |
|||
}; |
|||
}; |
|||
|
|||
memory@90000000 { |
|||
device_type = "memory"; |
|||
reg = <0x00000000 0x90000000 0x00000000 0x10000000>; |
|||
}; |
|||
|
|||
clint@2000000 { |
|||
compatible = "riscv,clint0"; |
|||
reg = <0x0 0x2000000 0x0 0xc0000>; |
|||
}; |
|||
|
|||
PLIC: plic@c000000 { |
|||
compatible = "riscv,plic0"; |
|||
reg = <0x0 0xc000000 0x0 0x1000000>; |
|||
riscv,ndev = <0x1f>; |
|||
riscv,max-priority = <0xf>; |
|||
#interrupt-cells = <1>; |
|||
interrupt-controller; |
|||
}; |
|||
|
|||
uart0: serial@10000000 { |
|||
compatible = "ns16550a"; |
|||
reg = <0x0 0x10000000 0x0 0x100>; |
|||
clock-frequency = <10000000>; |
|||
interrupts = <10>; |
|||
reg-shift = <0>; |
|||
reg-io-width = <1>; |
|||
}; |
|||
|
|||
}; |
|||
@ -0,0 +1,36 @@ |
|||
/dts-v1/; |
|||
|
|||
/ { |
|||
#address-cells = <2>; |
|||
#size-cells = <2>; |
|||
|
|||
cpus { // Container for all CPU cores |
|||
#address-cells = <1>; // CPU children use 1 cell for hartid |
|||
#size-cells = <0>; // CPU children don't specify size |
|||
timebase-frequency = <10000000>; // Timer ticks at 10 MHz (for OS timing) |
|||
|
|||
CPU0: cpu@0 { // First CPU core (label: CPU0, unit address: 0) |
|||
device_type = "cpu"; // Identifies this as a CPU device |
|||
reg = <0>; // Hartid = 0 (hardware thread ID) |
|||
riscv,isa = "rv64imafdc"; // ISA: RV64 with I,M,A,F,D,C extensions |
|||
}; |
|||
}; |
|||
|
|||
|
|||
memory@90000000 { |
|||
device_type = "memory"; |
|||
reg = <0x0 0x90000000 0x0 0x00010000>; |
|||
}; |
|||
|
|||
led1@10001000 { |
|||
compatible = "led_device"; |
|||
reg = <0x0 0x10001000 0x0 0x1000>; |
|||
spike,plugin-params = "16"; |
|||
}; |
|||
|
|||
led2@10002000 { |
|||
compatible = "led_device"; |
|||
reg = <0x0 0x10002000 0x0 0x1000>; |
|||
spike,plugin-params = "8"; |
|||
}; |
|||
}; |
|||
@ -0,0 +1,26 @@ |
|||
/dts-v1/; |
|||
|
|||
/ { |
|||
#address-cells = <2>; |
|||
#size-cells = <2>; |
|||
|
|||
cpus { // Container for all CPU cores |
|||
#address-cells = <1>; // CPU children use 1 cell for hartid |
|||
#size-cells = <0>; // CPU children don't specify size |
|||
timebase-frequency = <10000000>; // Timer ticks at 10 MHz (for OS timing) |
|||
|
|||
CPU0: cpu@0 { // First CPU core (label: CPU0, unit address: 0) |
|||
device_type = "cpu"; // Identifies this as a CPU device |
|||
reg = <0>; // Hartid = 0 (hardware thread ID) |
|||
riscv,isa = "rv64imafdc"; // ISA: RV64 with I,M,A,F,D,C extensions |
|||
}; |
|||
}; |
|||
|
|||
|
|||
led@10001000 { |
|||
compatible = "led_device"; |
|||
reg = <0x0 0x10001000 0x0 0x00001000>; |
|||
spike,plugin-params = "16"; |
|||
}; |
|||
|
|||
}; |
|||
@ -0,0 +1,23 @@ |
|||
/dts-v1/; |
|||
/ { |
|||
#address-cells = <1>; |
|||
#size-cells = <1>; |
|||
|
|||
cpus { // Container for all CPU cores |
|||
#address-cells = <1>; // CPU children use 1 cell for hartid |
|||
#size-cells = <0>; // CPU children don't specify size |
|||
timebase-frequency = <10000000>; // Timer ticks at 10 MHz (for OS timing) |
|||
|
|||
CPU0: cpu@0 { // First CPU core (label: CPU0, unit address: 0) |
|||
device_type = "cpu"; // Identifies this as a CPU device |
|||
reg = <0>; // Hartid = 0 (hardware thread ID) |
|||
riscv,isa = "rv64imafdc"; // ISA: RV64 with I,M,A,F,D,C extensions |
|||
}; |
|||
}; |
|||
|
|||
|
|||
memory@80000000 { |
|||
device_type = "memory"; |
|||
reg = <0x80000000 0x10000000>; // 256MB |
|||
}; |
|||
}; |
|||
@ -0,0 +1,24 @@ |
|||
/dts-v1/; |
|||
/ { |
|||
#address-cells = <2>; |
|||
#size-cells = <2>; |
|||
|
|||
cpus { // Container for all CPU cores |
|||
#address-cells = <1>; // CPU children use 1 cell for hartid |
|||
#size-cells = <0>; // CPU children don't specify size |
|||
timebase-frequency = <10000000>; // Timer ticks at 10 MHz (for OS timing) |
|||
|
|||
CPU0: cpu@0 { // First CPU core (label: CPU0, unit address: 0) |
|||
device_type = "cpu"; // Identifies this as a CPU device |
|||
reg = <0>; // Hartid = 0 (hardware thread ID) |
|||
riscv,isa = "rv64imafdc"; // ISA: RV64 with I,M,A,F,D,C extensions |
|||
}; |
|||
}; |
|||
|
|||
|
|||
memory@80000000 { |
|||
device_type = "memory"; |
|||
reg = <0x0 0x20000000 0x0 0x40000000>; // 1GB |
|||
}; |
|||
}; |
|||
|
|||
@ -0,0 +1,23 @@ |
|||
/dts-v1/; |
|||
/ { |
|||
#address-cells = <2>; |
|||
#size-cells = <1>; |
|||
|
|||
cpus { // Container for all CPU cores |
|||
#address-cells = <1>; // CPU children use 1 cell for hartid |
|||
#size-cells = <0>; // CPU children don't specify size |
|||
timebase-frequency = <10000000>; // Timer ticks at 10 MHz (for OS timing) |
|||
|
|||
CPU0: cpu@0 { // First CPU core (label: CPU0, unit address: 0) |
|||
device_type = "cpu"; // Identifies this as a CPU device |
|||
reg = <0>; // Hartid = 0 (hardware thread ID) |
|||
riscv,isa = "rv64imafdc"; // ISA: RV64 with I,M,A,F,D,C extensions |
|||
}; |
|||
}; |
|||
|
|||
|
|||
memory@100000000 { |
|||
device_type = "memory"; |
|||
reg = <0x1 0x00000000 0x20000000>; // 512MB at 4GB |
|||
}; |
|||
}; |
|||
@ -0,0 +1,28 @@ |
|||
/dts-v1/; |
|||
/ { |
|||
#address-cells = <2>; |
|||
#size-cells = <2>; |
|||
|
|||
cpus { // Container for all CPU cores |
|||
#address-cells = <1>; // CPU children use 1 cell for hartid |
|||
#size-cells = <0>; // CPU children don't specify size |
|||
timebase-frequency = <10000000>; // Timer ticks at 10 MHz (for OS timing) |
|||
|
|||
CPU0: cpu@0 { // First CPU core (label: CPU0, unit address: 0) |
|||
device_type = "cpu"; // Identifies this as a CPU device |
|||
reg = <0>; // Hartid = 0 (hardware thread ID) |
|||
riscv,isa = "rv64imafdc"; // ISA: RV64 with I,M,A,F,D,C extensions |
|||
}; |
|||
}; |
|||
|
|||
|
|||
memory@80000000 { |
|||
device_type = "memory"; |
|||
reg = <0x0 0x80000000 0x0 0x20000000>; |
|||
}; |
|||
|
|||
memory@A0000000 { |
|||
device_type = "memory"; |
|||
reg = <0x0 0xA0000000 0x0 0x20000000>; |
|||
}; |
|||
}; |
|||
@ -0,0 +1,263 @@ |
|||
#include <riscv/sim.h> |
|||
#include <riscv/dtb_discovery.h> |
|||
#include <riscv/devices.h> |
|||
#include <riscv/mmu.h> |
|||
#include <fdt/libfdt.h> |
|||
#include <fstream> |
|||
#include <iostream> |
|||
#include <map> |
|||
#include <vector> |
|||
#include <string> |
|||
#include <filesystem> |
|||
#include <cstring> |
|||
#include <dlfcn.h> |
|||
|
|||
#include "led-device.h" |
|||
|
|||
struct expected_mem_t { |
|||
reg_t base; |
|||
}; |
|||
|
|||
struct expected_device_t { |
|||
const std::type_info* compatible; |
|||
reg_t base; |
|||
}; |
|||
|
|||
struct test_case_t { |
|||
std::vector<expected_mem_t> memory; |
|||
std::vector<expected_device_t> devices; |
|||
}; |
|||
|
|||
// Key: pair of (filename, dtb_discovery_enabled)
|
|||
using test_key_t = std::pair<std::string, bool>; |
|||
|
|||
static std::map<test_key_t, test_case_t> test_cases_expected_results = { |
|||
// With DTB discovery enabled
|
|||
{ {"mem-64bit-single.dtb", true}, { |
|||
{{0x20000000ULL}}, |
|||
{{&typeid(debug_module_t), 0x0ULL}} |
|||
}}, |
|||
{ {"mem-64bit-single.dtb", false}, { |
|||
{}, |
|||
{{&typeid(debug_module_t), 0x0ULL}} |
|||
}}, |
|||
{ {"mem-32bit.dtb", true}, { |
|||
{{0x80000000ULL}}, |
|||
{{&typeid(debug_module_t), 0x0ULL}} |
|||
}}, |
|||
{ {"mem-32bit.dtb", false}, { |
|||
{}, |
|||
{{&typeid(debug_module_t), 0x0ULL}} |
|||
}}, |
|||
{ {"mem-multiple-nodes.dtb", true}, { |
|||
{{0x80000000ULL}, {0xA0000000ULL}}, |
|||
{{&typeid(debug_module_t), 0x0ULL}} |
|||
}}, |
|||
{ {"mem-multiple-nodes.dtb", false}, { |
|||
{}, |
|||
{{&typeid(debug_module_t), 0x0ULL}} |
|||
}}, |
|||
{ {"mem-mixed-cells-sizes.dtb", true}, { |
|||
{{0x100000000ULL}}, |
|||
{{&typeid(debug_module_t), 0x0ULL}} |
|||
}}, |
|||
{ {"mem-mixed-cells-sizes.dtb", false}, { |
|||
{}, |
|||
{{&typeid(debug_module_t), 0x0ULL}} |
|||
}}, |
|||
{ {"devices-single-led.dtb", true}, { |
|||
{}, |
|||
{{&typeid(debug_module_t), 0x0ULL}, |
|||
{&typeid(led_device_t), 0x10001000ULL}} |
|||
}}, |
|||
{ {"devices-single-led.dtb", false}, { |
|||
{}, |
|||
{{&typeid(debug_module_t), 0x0ULL}} |
|||
}}, |
|||
{ {"devices-multiple-leds.dtb", true}, { |
|||
{{0x90000000ULL}}, |
|||
{{&typeid(debug_module_t), 0x0ULL}, |
|||
{&typeid(led_device_t), 0x10001000ULL}, |
|||
{&typeid(led_device_t), 0x10002000ULL}} |
|||
}}, |
|||
{ {"devices-multiple-leds.dtb", false}, { |
|||
{}, |
|||
{{&typeid(debug_module_t), 0x0ULL}} |
|||
}}, |
|||
{ {"devices-clint-plic-uart.dtb", true}, { |
|||
{{0x90000000ULL}}, |
|||
{{&typeid(debug_module_t), 0x0ULL}, |
|||
{&typeid(clint_t), 0x2000000ULL}, |
|||
{&typeid(plic_t), 0xc000000ULL}, |
|||
{&typeid(ns16550_t), 0x10000000ULL}} |
|||
}}, |
|||
{ {"devices-clint-plic-uart.dtb", false}, { |
|||
{}, |
|||
{{&typeid(debug_module_t), 0x0ULL}, |
|||
{&typeid(clint_t), 0x2000000ULL}, |
|||
{&typeid(plic_t), 0xc000000ULL}, |
|||
{&typeid(ns16550_t), 0x10000000ULL}} |
|||
}} |
|||
}; |
|||
|
|||
bool check_memory_devices(const std::map<reg_t, abstract_device_t*>& devices, |
|||
const std::vector<expected_mem_t>& expected_memory) { |
|||
bool passed = true; |
|||
|
|||
for (const auto& mem : expected_memory) { |
|||
auto it = devices.find(mem.base); |
|||
|
|||
if (it == devices.end()) { |
|||
std::cerr << "Memory NOT found at 0x" << std::hex << mem.base << std::endl; |
|||
passed = false; |
|||
} else if (dynamic_cast<mem_t*>(it->second) == nullptr) { |
|||
std::cerr << "Device at 0x" << std::hex << mem.base |
|||
<< " is not mem_t type" << std::endl; |
|||
passed = false; |
|||
} else { |
|||
std::cout << "Memory found at 0x" << std::hex << mem.base << std::endl; |
|||
} |
|||
} |
|||
|
|||
return passed; |
|||
} |
|||
|
|||
bool check_devices(const std::map<reg_t, abstract_device_t*>& devices, |
|||
const std::vector<expected_device_t>& expected_devices) { |
|||
bool passed = true; |
|||
|
|||
for (const auto& expected_device : expected_devices) { |
|||
auto it = devices.find(expected_device.base); |
|||
|
|||
if (it == devices.end()) { |
|||
std::cerr << "Expected device not found at 0x" << std::hex << expected_device.base << std::endl; |
|||
passed = false; |
|||
continue; |
|||
} |
|||
|
|||
const std::type_info& actual_type = typeid(*it->second); |
|||
|
|||
if (*expected_device.compatible != actual_type) { |
|||
std::cerr << "Device at 0x" << std::hex << expected_device.base |
|||
<< " has wrong type. Expected: " << expected_device.compatible->name() |
|||
<< ", Got: " << actual_type.name() << std::endl; |
|||
passed = false; |
|||
} else { |
|||
std::cout << "Expected device found at 0x" << std::hex << expected_device.base |
|||
<< " Type: " << actual_type.name() << std::endl; |
|||
} |
|||
} |
|||
|
|||
return passed; |
|||
} |
|||
|
|||
bool check_no_unexpected_devices(const std::map<reg_t, abstract_device_t*>& devices, |
|||
const test_case_t& expected) { |
|||
size_t expected_count = expected.memory.size() + expected.devices.size(); |
|||
|
|||
if (devices.size() != expected_count) { |
|||
std::cerr << "Device count mismatch. Expected: " << expected_count |
|||
<< ", Got: " << devices.size() << std::endl; |
|||
return false; |
|||
} |
|||
|
|||
return true; |
|||
} |
|||
|
|||
bool compare_devices(sim_t& sim, const test_case_t& expected) { |
|||
const auto& devices = sim.get_bus().get_devices(); |
|||
|
|||
bool no_extra_ok = check_no_unexpected_devices(devices, expected); |
|||
bool memory_ok = check_memory_devices(devices, expected.memory); |
|||
bool devices_ok= check_devices(devices, expected.devices); |
|||
|
|||
return memory_ok && devices_ok && no_extra_ok; |
|||
} |
|||
|
|||
bool run_test(const std::string& dtb_path, bool dtb_discovery, |
|||
const std::string& pk, const std::string& executable) { |
|||
// Extract filename from dtb path
|
|||
std::filesystem::path p(dtb_path); |
|||
std::string dtb_filename = p.filename().string(); |
|||
|
|||
// Create test key
|
|||
test_key_t test_key = {dtb_filename, dtb_discovery}; |
|||
|
|||
// Find expected layout
|
|||
auto test_it = test_cases_expected_results.find(test_key); |
|||
if (test_it == test_cases_expected_results.end()) { |
|||
std::cout << "Error: No expected result for '" << dtb_filename |
|||
<< "' with dtb_discovery=" << (dtb_discovery ? "true" : "false") << "\n"; |
|||
return false; |
|||
} |
|||
|
|||
const auto& expected = test_it->second; |
|||
std::cout << "Testing: " << dtb_filename << "\n"; |
|||
std::cout << "DTB Discovery: " << (dtb_discovery ? "enabled" : "disabled") << "\n"; |
|||
|
|||
// Setup simulator
|
|||
cfg_t cfg; |
|||
std::vector<device_factory_sargs_t> devices; |
|||
std::vector<std::string> htif_args{pk, executable}; |
|||
debug_module_config_t dm_config; |
|||
std::vector<std::pair<reg_t, abstract_mem_t*>> mems; |
|||
|
|||
sim_t sim(&cfg, false, |
|||
mems, |
|||
devices, |
|||
dtb_discovery, |
|||
htif_args, |
|||
dm_config, |
|||
nullptr, |
|||
true, |
|||
dtb_path.c_str(), |
|||
false, |
|||
nullptr, |
|||
std::nullopt); |
|||
|
|||
// Compare devices
|
|||
bool result = compare_devices(sim, expected); |
|||
|
|||
if (result) { |
|||
std::cout << "✓ TEST PASSED: " << dtb_filename |
|||
<< " (dtb_discovery=" << (dtb_discovery ? "enabled" : "disabled") << ")\n\n"; |
|||
} else { |
|||
std::cout << "✗ TEST FAILED: " << dtb_filename |
|||
<< " (dtb_discovery=" << (dtb_discovery ? "enabled" : "disabled") << ")\n\n"; |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
|
|||
int main(int argc, char **argv) { |
|||
if (argc != 4) { |
|||
std::cerr << "Usage: " << argv[0] << " --dtb=<file> <pk> <executable>\n"; |
|||
return 1; |
|||
} |
|||
|
|||
std::string dtb_path; |
|||
std::string pk; |
|||
std::string executable; |
|||
|
|||
// Parse arguments
|
|||
int arg_idx = 1; |
|||
|
|||
// First argument must be --dtb=
|
|||
std::string dtb_arg = argv[arg_idx++]; |
|||
if (dtb_arg.find("--dtb=") == 0) { |
|||
dtb_path = dtb_arg.substr(strlen("--dtb=")); |
|||
} else { |
|||
std::cerr << "Error: First argument must be --dtb=<file>\n"; |
|||
return 1; |
|||
} |
|||
|
|||
// Remaining arguments are pk and executable
|
|||
pk = argv[arg_idx++]; |
|||
executable = argv[arg_idx]; |
|||
|
|||
// Run tests with both dtb_discovery values
|
|||
bool test_dtb_discovery_disabled = run_test(dtb_path, false, pk, executable); |
|||
bool test_dtb_discovery_enabled = run_test(dtb_path, true, pk, executable); |
|||
|
|||
return !(test_dtb_discovery_disabled && test_dtb_discovery_enabled); |
|||
} |
|||
@ -0,0 +1,142 @@ |
|||
#include "dtb_discovery.h" |
|||
#include "libfdt.h" |
|||
#include "dts.h" |
|||
#include <iostream> |
|||
#include <sstream> |
|||
#include <cstdlib> |
|||
|
|||
|
|||
namespace { |
|||
std::vector<std::string> split_csv(const std::string& str) { |
|||
std::vector<std::string> result; |
|||
std::stringstream ss(str); |
|||
std::string item; |
|||
while (std::getline(ss, item, ',')) { |
|||
result.push_back(item); |
|||
} |
|||
return result; |
|||
} |
|||
|
|||
std::string join_csv(const std::vector<std::string>& vec) { |
|||
std::string result; |
|||
for (size_t i = 0; i < vec.size(); ++i) { |
|||
result += vec[i]; |
|||
if (i < vec.size() - 1) result += ","; |
|||
} |
|||
return result; |
|||
} |
|||
|
|||
std::vector<std::string> get_device_params(const void* fdt, int offset) { |
|||
int param_len; |
|||
const char* params = (char*)fdt_getprop(fdt, offset, "spike,plugin-params", ¶m_len); |
|||
return params ? split_csv(params) : std::vector<std::string>(); |
|||
} |
|||
|
|||
// Helper to parse reg property
|
|||
bool parse_reg_property(const void* fdt, int offset, const char* node_name, |
|||
uint64_t& base, uint64_t& size) { |
|||
int len = 0; |
|||
const fdt32_t* reg_prop = (const fdt32_t*)fdt_getprop(fdt, offset, "reg", &len); |
|||
if (!reg_prop) { |
|||
fprintf(stderr, "DTB node '%s' missing 'reg' property\n", node_name); |
|||
return false; |
|||
} |
|||
|
|||
int addr_cells = fdt_address_cells(fdt, 0); |
|||
int size_cells = fdt_size_cells(fdt, 0); |
|||
if (addr_cells < 1 || size_cells < 1) { |
|||
fprintf(stderr, "Invalid #address-cells or #size-cells for node '%s'\n", node_name); |
|||
return false; |
|||
} |
|||
|
|||
int entry_cells = addr_cells + size_cells; |
|||
int entry_size = entry_cells * sizeof(fdt32_t); |
|||
if ((len % entry_size) != 0) { |
|||
fprintf(stderr, "DTB node '%s' has malformed 'reg'\n", node_name); |
|||
return false; |
|||
} |
|||
|
|||
const fdt32_t* p = reg_prop; |
|||
base = 0; |
|||
size = 0; |
|||
|
|||
// Parse base address
|
|||
for (int i = 0; i < addr_cells; ++i) |
|||
base = (base << 32) | fdt32_to_cpu(p[i]); |
|||
|
|||
// Parse size
|
|||
for (int i = 0; i < size_cells; ++i) |
|||
size = (size << 32) | fdt32_to_cpu(p[addr_cells + i]); |
|||
|
|||
return true; |
|||
} |
|||
|
|||
// Core platform devices handled by Spike internally
|
|||
const std::set<std::string> SPIKE_DEFAULT_DEVICES = { |
|||
"riscv,plic0", |
|||
"sifive,plic-1.0.0", |
|||
"ns16550a", |
|||
"riscv,clint0" |
|||
}; |
|||
|
|||
bool should_skip_device(const std::string& device_name) { |
|||
return SPIKE_DEFAULT_DEVICES.find(device_name) != SPIKE_DEFAULT_DEVICES.end(); |
|||
} |
|||
} // namespace
|
|||
|
|||
namespace dtb_discovery { |
|||
|
|||
void discover_devices_from_dtb(const void* fdt, |
|||
std::vector<device_factory_sargs_t>& factories) { |
|||
for (const auto& [device_name, factory] : mmio_device_map()) { |
|||
//Spike default devices are not managed by dtb discovery feature
|
|||
if (should_skip_device(device_name)) continue; |
|||
|
|||
int offset = -1; |
|||
while ((offset = fdt_node_offset_by_compatible(fdt, offset, device_name.c_str())) >= 0) { |
|||
const char* node_name = fdt_get_name(fdt, offset, NULL); |
|||
|
|||
uint64_t base = 0, size = 0; |
|||
if (!parse_reg_property(fdt, offset, node_name, base, size)) { |
|||
exit(1); |
|||
} |
|||
|
|||
auto parsed_args = get_device_params(fdt, offset); |
|||
|
|||
// Prepend base and size to arguments
|
|||
std::vector<std::string> full_args; |
|||
full_args.push_back(std::to_string(base)); |
|||
full_args.push_back(std::to_string(size)); |
|||
full_args.insert(full_args.end(), parsed_args.begin(), parsed_args.end()); |
|||
|
|||
fprintf(stdout, "DTB discovered device: %s - type: %s - base: 0x%016" PRIx64 |
|||
" - size: 0x%016" PRIx64 " - params: %s\n", |
|||
node_name, device_name.c_str(), base, size, |
|||
(parsed_args.empty() ? "" : join_csv(parsed_args).c_str())); |
|||
|
|||
factories.push_back({factory, full_args}); |
|||
} |
|||
} |
|||
} |
|||
|
|||
void discover_memory_from_dtb(const void* fdt, |
|||
std::vector<std::pair<reg_t, abstract_mem_t*>>& mems) { |
|||
int offset = -1; |
|||
while ((offset = fdt_node_offset_by_prop_value( |
|||
fdt, offset, "device_type", "memory", sizeof("memory"))) >= 0) { |
|||
const char* node_name = fdt_get_name(fdt, offset, nullptr); |
|||
|
|||
uint64_t base = 0, size = 0; |
|||
if (!parse_reg_property(fdt, offset, node_name, base, size)) { |
|||
exit(1); |
|||
} |
|||
|
|||
fprintf(stdout, |
|||
"DTB memory device: %s - Memory initialized at [0x%016" PRIx64 ", 0x%016" PRIx64 ")\n", |
|||
node_name, base, base + size); |
|||
|
|||
mems.emplace_back(base, new mem_t(size)); |
|||
} |
|||
} |
|||
|
|||
} // namespace dtb_discovery
|
|||
@ -0,0 +1,21 @@ |
|||
#ifndef _RISCV_DTB_DISCOVERY_H |
|||
#define _RISCV_DTB_DISCOVERY_H |
|||
|
|||
#include "sim.h" |
|||
#include <vector> |
|||
#include <string> |
|||
|
|||
class bus_t; |
|||
class abstract_mem_t; |
|||
|
|||
namespace dtb_discovery { |
|||
|
|||
void discover_devices_from_dtb(const void* fdt, |
|||
std::vector<device_factory_sargs_t>& factories); |
|||
|
|||
void discover_memory_from_dtb(const void* fdt, std::vector<std::pair<reg_t, abstract_mem_t*>>& mems); |
|||
|
|||
} // namespace dtb_discovery
|
|||
|
|||
|
|||
#endif // _RISCV_DTB_DISCOVERY_H
|
|||
Loading…
Reference in new issue