Browse Source

DTB discovery feature

pull/2165/head
Francesco Scappatura 1 month ago
parent
commit
2c94ea431e
  1. 3
      ci-tests/custom-csr.cc
  2. 99
      ci-tests/dtb-discovery/devices-sample/led-device.cc
  3. 26
      ci-tests/dtb-discovery/devices-sample/led-device.h
  4. 52
      ci-tests/dtb-discovery/dts-samples/devices-clint-plic-uart.dts
  5. 36
      ci-tests/dtb-discovery/dts-samples/devices-multiple-leds.dts
  6. 26
      ci-tests/dtb-discovery/dts-samples/devices-single-led.dts
  7. 23
      ci-tests/dtb-discovery/dts-samples/mem-32bit.dts
  8. 24
      ci-tests/dtb-discovery/dts-samples/mem-64bit-single.dts
  9. 23
      ci-tests/dtb-discovery/dts-samples/mem-mixed-cells-sizes.dts
  10. 28
      ci-tests/dtb-discovery/dts-samples/mem-multiple-nodes.dts
  11. 263
      ci-tests/dtb-discovery/test-dtb-discovery.cc
  12. 3
      ci-tests/test-customext.cc
  13. 16
      ci-tests/test-spike
  14. 2
      ci-tests/testlib.cc
  15. 4
      riscv/devices.cc
  16. 1
      riscv/devices.h
  17. 142
      riscv/dtb_discovery.cc
  18. 21
      riscv/dtb_discovery.h
  19. 2
      riscv/riscv.mk.in
  20. 32
      riscv/sim.cc
  21. 3
      riscv/sim.h
  22. 16
      spike_main/spike.cc

3
ci-tests/custom-csr.cc

@ -71,7 +71,8 @@ int main(int argc, char **argv) {
.support_impebreak = true};
std::vector<std::pair<reg_t, abstract_mem_t *>> mems =
make_mems(cfg.mem_layout);
sim_t sim(&cfg, false, mems, plugin_devices, htif_args, dm_config,
bool dtb_discovery=false;
sim_t sim(&cfg, false, mems, plugin_devices, dtb_discovery, htif_args, dm_config,
nullptr, // log_path
true, // dtb_enabled
nullptr, // dtb_file

99
ci-tests/dtb-discovery/devices-sample/led-device.cc

@ -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)

26
ci-tests/dtb-discovery/devices-sample/led-device.h

@ -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

52
ci-tests/dtb-discovery/dts-samples/devices-clint-plic-uart.dts

@ -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>;
};
};

36
ci-tests/dtb-discovery/dts-samples/devices-multiple-leds.dts

@ -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";
};
};

26
ci-tests/dtb-discovery/dts-samples/devices-single-led.dts

@ -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";
};
};

23
ci-tests/dtb-discovery/dts-samples/mem-32bit.dts

@ -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
};
};

24
ci-tests/dtb-discovery/dts-samples/mem-64bit-single.dts

@ -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
};
};

23
ci-tests/dtb-discovery/dts-samples/mem-mixed-cells-sizes.dts

@ -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
};
};

28
ci-tests/dtb-discovery/dts-samples/mem-multiple-nodes.dts

@ -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>;
};
};

263
ci-tests/dtb-discovery/test-dtb-discovery.cc

@ -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);
}

3
ci-tests/test-customext.cc

@ -80,7 +80,8 @@ int main(int argc, char **argv) {
.support_impebreak = true};
std::vector<std::pair<reg_t, abstract_mem_t *>> mems =
make_mems(cfg.mem_layout);
sim_t sim(&cfg, false, mems, plugin_devices, htif_args, dm_config,
bool dtb_discovery=false;
sim_t sim(&cfg, false, mems, plugin_devices, dtb_discovery, htif_args, dm_config,
nullptr, // log_path
true, // dtb_enabled
nullptr, // dtb_file

16
ci-tests/test-spike

@ -37,6 +37,7 @@ tar xf snippy-x86_64-linux.tar.xz
bin/llvm-snippy --version | grep "Snippy version: 2.1.0"
PATH="$PATH:$RUN/bin" "$ROOT"/ci-tests/run-snippy-tests.sh "$RUN" "$ROOT"/ci-tests/snippy-tests "$INSTALL"/bin/spike
# check that including sim.h in an external project works
g++ -std=c++2a -I$INSTALL/include -L$INSTALL/lib $CI/testlib.cc -lriscv -o test-libriscv
g++ -std=c++2a -I$INSTALL/include -L$INSTALL/lib $CI/test-customext.cc -lriscv -o test-customext
@ -52,3 +53,18 @@ $INSTALL/bin/spike --log-commits --isa=rv64gc $BUILD/pk/pk atomics 2> /dev/null
LD_LIBRARY_PATH=$INSTALL/lib ./test-libriscv $BUILD/pk/pk hello | grep "Hello, world! Pi is approximately 3.141588."
LD_LIBRARY_PATH=$INSTALL/lib ./test-customext $BUILD/pk/pk dummy-slliuw | grep "Executed successfully"
LD_LIBRARY_PATH=$INSTALL/lib ./test-custom-csr $BUILD/pk/pk customcsr | grep "Executed successfully"
echo "DTB discovery tests"
g++ -std=c++2a -I$INSTALL/include -I$CI/dtb-discovery/devices-sample -L$INSTALL/lib $CI/dtb-discovery/test-dtb-discovery.cc $CI/dtb-discovery/devices-sample/led-device.cc -lriscv -o test-dtb-discovery
#compile sample dts
DTS_DIR="$CI/dtb-discovery/dts-samples"
TMP_DTB_DIR="/tmp/dtb-tests"
mkdir -p "$TMP_DTB_DIR"
DTB_LIST=()
#For each dts execute the test
for dts in "$DTS_DIR"/*.dts; do
name=$(basename "$dts" .dts)
dtb="$TMP_DTB_DIR/$name.dtb"
dtc -I dts -O dtb "$dts" -o "$dtb"
LD_LIBRARY_PATH=$INSTALL/lib ./test-dtb-discovery --dtb="$dtb" $BUILD/pk/pk hello
done

2
ci-tests/testlib.cc

@ -25,9 +25,11 @@ int main(int argc, char **argv) {
debug_module_config_t dm_config;
std::vector<std::pair<reg_t, abstract_mem_t*>> mems =
make_mems(cfg.mem_layout);
bool dtb_discovery=false;
sim_t sim(&cfg, false,
mems,
plugin_devices,
dtb_discovery,
htif_args,
dm_config,
nullptr,

4
riscv/devices.cc

@ -96,6 +96,10 @@ std::pair<reg_t, abstract_device_t*> bus_t::find_device(reg_t addr, size_t len)
return std::make_pair(0, fallback);
}
const std::map<reg_t, abstract_device_t*>& bus_t::get_devices() const {
return devices;
}
mem_t::mem_t(reg_t size)
: sz(size)
{

1
riscv/devices.h

@ -27,6 +27,7 @@ class bus_t : public abstract_device_t {
void add_device(reg_t addr, abstract_device_t* dev);
std::pair<reg_t, abstract_device_t*> find_device(reg_t addr, size_t len);
const std::map<reg_t, abstract_device_t*>& get_devices() const;
private:
std::map<reg_t, abstract_device_t*> devices;

142
riscv/dtb_discovery.cc

@ -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", &param_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

21
riscv/dtb_discovery.h

@ -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

2
riscv/riscv.mk.in

@ -28,6 +28,7 @@ riscv_install_hdrs = \
debug_rom_defines.h \
decode.h \
devices.h \
dtb_discovery.h \
disasm.h \
dts.h \
encoding.h \
@ -63,6 +64,7 @@ riscv_srcs = \
extensions.cc \
rocc.cc \
devices.cc \
dtb_discovery.cc \
rom.cc \
clint.cc \
plic.cc \

32
riscv/sim.cc

@ -1,6 +1,7 @@
// See LICENSE for license details.
#include "config.h"
#include "dtb_discovery.h"
#include "sim.h"
#include "mmu.h"
#include "dts.h"
@ -39,6 +40,7 @@ extern device_factory_t* ns16550_factory;
sim_t::sim_t(const cfg_t *cfg, bool halted,
std::vector<std::pair<reg_t, abstract_mem_t*>> mems,
const std::vector<device_factory_sargs_t>& plugin_device_factories,
const bool dtb_discovery,
const std::vector<std::string>& args,
const debug_module_config_t &dm_config,
const char *log_path,
@ -49,6 +51,7 @@ sim_t::sim_t(const cfg_t *cfg, bool halted,
: htif_t(args),
cfg(cfg),
mems(mems),
dtb_discovery(dtb_discovery),
dtb_enabled(dtb_enabled),
log_file(log_path),
cmd_file(cmd_file),
@ -66,9 +69,6 @@ sim_t::sim_t(const cfg_t *cfg, bool halted,
sout_.rdbuf(std::cerr.rdbuf()); // debug output goes to stderr by default
for (auto& x : mems)
bus.add_device(x.first, x.second);
bus.add_device(DEBUG_START, &debug_module);
socketif = NULL;
@ -121,9 +121,6 @@ sim_t::sim_t(const cfg_t *cfg, bool halted,
{clint_factory, {}},
{plic_factory, {}},
{ns16550_factory, {}}};
device_factories.insert(device_factories.end(),
plugin_device_factories.begin(),
plugin_device_factories.end());
// Load dtb_file if provided, otherwise self-generate a dts/dtb
if (dtb_file) {
@ -239,6 +236,29 @@ sim_t::sim_t(const cfg_t *cfg, bool halted,
cpu_idx++;
}
if (dtb_discovery)
{
//Add dtb discovered devices
std::vector<device_factory_sargs_t> dtb_discovery_plugin_device_factories;
dtb_discovery::discover_devices_from_dtb(fdt, dtb_discovery_plugin_device_factories);
device_factories.insert(device_factories.end(),
dtb_discovery_plugin_device_factories.begin(),
dtb_discovery_plugin_device_factories.end());
//Remove default memories and use dtb discovered memories
mems.clear();
dtb_discovery::discover_memory_from_dtb(fdt, mems);
}
//clint, plic, ns16550 are always discovered via dtb, independently from the --dtb_discovery flag
device_factories.insert(device_factories.end(),
plugin_device_factories.begin(),
plugin_device_factories.end());
for (auto& x : mems)
{
bus.add_device(x.first, x.second);
}
// must be located after procs/harts are set (devices might use sim_t get_* member functions)
for (size_t i = 0; i < device_factories.size(); i++) {
const device_factory_t* factory = device_factories[i].first;

3
riscv/sim.h

@ -32,6 +32,7 @@ public:
sim_t(const cfg_t *cfg, bool halted,
std::vector<std::pair<reg_t, abstract_mem_t*>> mems,
const std::vector<device_factory_sargs_t>& plugin_device_factories,
const bool dtb_discovery,
const std::vector<std::string>& args,
const debug_module_config_t &dm_config, const char *log_path,
bool dtb_enabled, const char *dtb_file,
@ -61,6 +62,7 @@ public:
virtual const cfg_t &get_cfg() const override { return *cfg; }
virtual const std::map<size_t, processor_t*>& get_harts() const override { return harts; }
const bus_t& get_bus() const { return bus;}
// Callback for processors to let the simulation know they were reset.
virtual void proc_reset(unsigned id) override;
@ -78,6 +80,7 @@ private:
std::pair<reg_t, reg_t> initrd_range;
std::string dts;
std::string dtb;
bool dtb_discovery;
bool dtb_enabled;
std::vector<std::shared_ptr<abstract_device_t>> devices;
std::shared_ptr<clint_t> clint;

16
spike_main/spike.cc

@ -53,6 +53,7 @@ static void help(int exit_code = 1)
fprintf(stderr, " --big-endian Use a big-endian memory system.\n");
fprintf(stderr, " --device=<name> Attach MMIO plugin device from an --extlib library,\n");
fprintf(stderr, " specify --device=<name>,<args> to pass down extra args.\n");
fprintf(stderr, " --dtb-discovery Enable direct device discovery from device tree blob. Requires --dtb and usage of special \"spike_plugin_params\" dts field.\n");
fprintf(stderr, " --log-cache-miss Generate a log of cache miss\n");
fprintf(stderr, " --log-commits Generate a log of commits info\n");
fprintf(stderr, " --extension=<name> Specify RoCC Extension\n");
@ -324,6 +325,8 @@ int main(int argc, char** argv)
bool UNUSED socket = false; // command line option -s
bool dump_dts = false;
bool dtb_enabled = true;
bool dtb_discovery = false;
bool memory_option = false;
const char* kernel = NULL;
reg_t kernel_offset, kernel_size;
std::vector<device_factory_sargs_t> plugin_device_factories;
@ -377,7 +380,7 @@ int main(int argc, char** argv)
parser.option('s', 0, 0, [&](const char UNUSED *s){socket = true;});
#endif
parser.option('p', 0, 1, [&](const char* s){nprocs = atoul_nonzero_safe(s);});
parser.option('m', 0, 1, [&](const char* s){cfg.mem_layout = parse_mem_layout(s);});
parser.option('m', 0, 1, [&](const char* s){cfg.mem_layout = parse_mem_layout(s); memory_option=true; });
parser.option(0, "halted", 0, [&](const char UNUSED *s){halted = true;});
parser.option(0, "rbb-port", 1, [&](const char* s){use_rbb = true; rbb_port = atoul_safe(s);});
parser.option(0, "pc", 1, [&](const char* s){cfg.start_pc = strtoull(s, 0, 0);});
@ -395,6 +398,7 @@ int main(int argc, char** argv)
parser.option(0, "pmpgranularity", 1, [&](const char* s){cfg.pmpgranularity = atoul_safe(s);});
parser.option(0, "priv", 1, [&](const char* s){cfg.priv = s;});
parser.option(0, "device", 1, device_parser);
parser.option(0, "dtb-discovery", 0, [&](const char UNUSED *s){ dtb_discovery = true;} );
parser.option(0, "extension", 1, [&](const char* s){extensions.push_back(find_extension(s));});
parser.option(0, "dump-dts", 0, [&](const char UNUSED *s){dump_dts = true;});
parser.option(0, "disable-dtb", 0, [&](const char UNUSED *s){dtb_enabled = false;});
@ -518,8 +522,16 @@ int main(int argc, char** argv)
cfg.hartids = default_hartids;
}
if (dtb_discovery){
if (memory_option) { std::cerr << "--dtb-discovery option is not compatible with --memory/-m;."<<std::endl; exit(1);}
if (!plugin_device_factories.empty()) { std::cerr << "--dtb-discovery option is not compatible with --device option."<<std::endl; exit(1);}
if (dtb_file==NULL) { std::cerr << "--dtb-discovery option required a dtb_file. Use --dtb option."<<std::endl; exit(1);}
if (dtb_enabled==false) { std::cerr << "--dtb-discovery option is not compatible with --disable-dtb"<<std::endl; exit(1);}
}
sim_t s(&cfg, halted,
mems, plugin_device_factories, htif_args, dm_config, log_path, dtb_enabled, dtb_file,
mems, plugin_device_factories, dtb_discovery, htif_args, dm_config, log_path, dtb_enabled, dtb_file,
socket,
cmd_file,
instructions);

Loading…
Cancel
Save