diff --git a/ci-tests/custom-csr.cc b/ci-tests/custom-csr.cc index 89b0149b..94857611 100644 --- a/ci-tests/custom-csr.cc +++ b/ci-tests/custom-csr.cc @@ -71,7 +71,8 @@ int main(int argc, char **argv) { .support_impebreak = true}; std::vector> 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 diff --git a/ci-tests/dtb-discovery/devices-sample/led-device.cc b/ci-tests/dtb-discovery/devices-sample/led-device.cc new file mode 100644 index 00000000..9f732655 --- /dev/null +++ b/ci-tests/dtb-discovery/devices-sample/led-device.cc @@ -0,0 +1,99 @@ +#include "led-device.h" +#include +#include +#include +#include + +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 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 &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) diff --git a/ci-tests/dtb-discovery/devices-sample/led-device.h b/ci-tests/dtb-discovery/devices-sample/led-device.h new file mode 100644 index 00000000..0c0e5cbe --- /dev/null +++ b/ci-tests/dtb-discovery/devices-sample/led-device.h @@ -0,0 +1,26 @@ +#ifndef LED_DEVICE_H +#define LED_DEVICE_H + +#include +#include +#include + +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 diff --git a/ci-tests/dtb-discovery/dts-samples/devices-clint-plic-uart.dts b/ci-tests/dtb-discovery/dts-samples/devices-clint-plic-uart.dts new file mode 100644 index 00000000..7b69bbc5 --- /dev/null +++ b/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>; + }; + +}; diff --git a/ci-tests/dtb-discovery/dts-samples/devices-multiple-leds.dts b/ci-tests/dtb-discovery/dts-samples/devices-multiple-leds.dts new file mode 100644 index 00000000..421f34dd --- /dev/null +++ b/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"; + }; +}; diff --git a/ci-tests/dtb-discovery/dts-samples/devices-single-led.dts b/ci-tests/dtb-discovery/dts-samples/devices-single-led.dts new file mode 100644 index 00000000..ff12cf66 --- /dev/null +++ b/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"; + }; + +}; diff --git a/ci-tests/dtb-discovery/dts-samples/mem-32bit.dts b/ci-tests/dtb-discovery/dts-samples/mem-32bit.dts new file mode 100644 index 00000000..0afe149e --- /dev/null +++ b/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 + }; +}; diff --git a/ci-tests/dtb-discovery/dts-samples/mem-64bit-single.dts b/ci-tests/dtb-discovery/dts-samples/mem-64bit-single.dts new file mode 100644 index 00000000..1873dc77 --- /dev/null +++ b/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 + }; +}; + diff --git a/ci-tests/dtb-discovery/dts-samples/mem-mixed-cells-sizes.dts b/ci-tests/dtb-discovery/dts-samples/mem-mixed-cells-sizes.dts new file mode 100644 index 00000000..89fdb62a --- /dev/null +++ b/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 + }; +}; diff --git a/ci-tests/dtb-discovery/dts-samples/mem-multiple-nodes.dts b/ci-tests/dtb-discovery/dts-samples/mem-multiple-nodes.dts new file mode 100644 index 00000000..75707d15 --- /dev/null +++ b/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>; + }; +}; diff --git a/ci-tests/dtb-discovery/test-dtb-discovery.cc b/ci-tests/dtb-discovery/test-dtb-discovery.cc new file mode 100644 index 00000000..e0185119 --- /dev/null +++ b/ci-tests/dtb-discovery/test-dtb-discovery.cc @@ -0,0 +1,263 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 memory; + std::vector devices; +}; + +// Key: pair of (filename, dtb_discovery_enabled) +using test_key_t = std::pair; + +static std::map 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& devices, + const std::vector& 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(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& devices, + const std::vector& 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& 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 devices; + std::vector htif_args{pk, executable}; + debug_module_config_t dm_config; + std::vector> 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= \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=\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); +} diff --git a/ci-tests/test-customext.cc b/ci-tests/test-customext.cc index 90cdb35a..2c4bbc2f 100644 --- a/ci-tests/test-customext.cc +++ b/ci-tests/test-customext.cc @@ -80,7 +80,8 @@ int main(int argc, char **argv) { .support_impebreak = true}; std::vector> 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 diff --git a/ci-tests/test-spike b/ci-tests/test-spike index dc734642..342ce8e7 100755 --- a/ci-tests/test-spike +++ b/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 diff --git a/ci-tests/testlib.cc b/ci-tests/testlib.cc index eb91d970..7a32f8e2 100644 --- a/ci-tests/testlib.cc +++ b/ci-tests/testlib.cc @@ -25,9 +25,11 @@ int main(int argc, char **argv) { debug_module_config_t dm_config; std::vector> 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, diff --git a/riscv/devices.cc b/riscv/devices.cc index b816ca12..971d850a 100644 --- a/riscv/devices.cc +++ b/riscv/devices.cc @@ -96,6 +96,10 @@ std::pair bus_t::find_device(reg_t addr, size_t len) return std::make_pair(0, fallback); } +const std::map& bus_t::get_devices() const { + return devices; +} + mem_t::mem_t(reg_t size) : sz(size) { diff --git a/riscv/devices.h b/riscv/devices.h index ccb5c9be..9a09df52 100644 --- a/riscv/devices.h +++ b/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 find_device(reg_t addr, size_t len); + const std::map& get_devices() const; private: std::map devices; diff --git a/riscv/dtb_discovery.cc b/riscv/dtb_discovery.cc new file mode 100644 index 00000000..abf4e21f --- /dev/null +++ b/riscv/dtb_discovery.cc @@ -0,0 +1,142 @@ +#include "dtb_discovery.h" +#include "libfdt.h" +#include "dts.h" +#include +#include +#include + + +namespace { + std::vector split_csv(const std::string& str) { + std::vector 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& 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 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(); + } + + // 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 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& 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 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>& 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 diff --git a/riscv/dtb_discovery.h b/riscv/dtb_discovery.h new file mode 100644 index 00000000..7e0d1ee5 --- /dev/null +++ b/riscv/dtb_discovery.h @@ -0,0 +1,21 @@ +#ifndef _RISCV_DTB_DISCOVERY_H +#define _RISCV_DTB_DISCOVERY_H + +#include "sim.h" +#include +#include + +class bus_t; +class abstract_mem_t; + +namespace dtb_discovery { + +void discover_devices_from_dtb(const void* fdt, + std::vector& factories); + +void discover_memory_from_dtb(const void* fdt, std::vector>& mems); + +} // namespace dtb_discovery + + +#endif // _RISCV_DTB_DISCOVERY_H diff --git a/riscv/riscv.mk.in b/riscv/riscv.mk.in index 3171e47c..ae87a4d7 100644 --- a/riscv/riscv.mk.in +++ b/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 \ diff --git a/riscv/sim.cc b/riscv/sim.cc index 7b07bd67..ca20cbf8 100644 --- a/riscv/sim.cc +++ b/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> mems, const std::vector& plugin_device_factories, + const bool dtb_discovery, const std::vector& 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 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; diff --git a/riscv/sim.h b/riscv/sim.h index 299e8965..fd2f993f 100644 --- a/riscv/sim.h +++ b/riscv/sim.h @@ -32,6 +32,7 @@ public: sim_t(const cfg_t *cfg, bool halted, std::vector> mems, const std::vector& plugin_device_factories, + const bool dtb_discovery, const std::vector& 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& 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 initrd_range; std::string dts; std::string dtb; + bool dtb_discovery; bool dtb_enabled; std::vector> devices; std::shared_ptr clint; diff --git a/spike_main/spike.cc b/spike_main/spike.cc index 2502dcb2..f62d1daf 100644 --- a/spike_main/spike.cc +++ b/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= Attach MMIO plugin device from an --extlib library,\n"); fprintf(stderr, " specify --device=, 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= 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 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;."<