diff --git a/zephcore/CMakeLists.txt b/zephcore/CMakeLists.txt index 897eaad..9ddbce5 100644 --- a/zephcore/CMakeLists.txt +++ b/zephcore/CMakeLists.txt @@ -425,6 +425,13 @@ target_sources(app PRIVATE helpers/fatal_reboot.c ) +# Native-Linux (native_sim) runtime setup: force real-time clock mode so the +# simulated clock tracks wall time (required for the real SX126x radio's +# BUSY/DIO1 timing). Bakes in the equivalent of the --rt command-line flag. +if(CONFIG_ARCH_POSIX) + target_sources(app PRIVATE adapters/transport/linux_native_setup.c) +endif() + # ========== Radio Driver Selection ========== if(CONFIG_ZEPHCORE_RADIO_LR1110) message(STATUS "ZephCore Radio: LR1110 (Zephyr LoRa driver)") diff --git a/zephcore/adapters/transport/linux_native_setup.c b/zephcore/adapters/transport/linux_native_setup.c new file mode 100644 index 0000000..da2663b --- /dev/null +++ b/zephcore/adapters/transport/linux_native_setup.c @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2026 ZephCore + * SPDX-License-Identifier: Apache-2.0 + * + * Native-Linux (native_sim) runtime setup. + * + * Forces the native_simulator's simulated clock into real-time mode so it + * tracks host wall-clock time. This is REQUIRED for ZephCore on native + * Linux: unlike a pure simulation, we drive a REAL SX126x radio over + * SPI/GPIO, and the radio's BUSY / DIO1 (CAD, TX-done, RX-done) signals + * arrive in real time. Without real-time mode the simulated clock races + * ahead of the hardware and every radio timeout fires prematurely (CAD + * -ETIMEDOUT, TX/RX stalls). Equivalent to always passing --rt, so the + * binary no longer needs that flag. + */ + +#include + +#include + +/* Provided by the native_simulator timer model (nsi_timer_model.h); declared + * here directly to avoid pulling the native_simulator-internal include path + * into this translation unit. */ +extern void hwtimer_set_real_time_mode(bool new_rt); + +static void zephcore_native_force_realtime(void) +{ + hwtimer_set_real_time_mode(true); +} + +/* PRE_BOOT_2: runs after command-line parsing but before the HW models + * (incl. the timer model) are initialized, so real-time mode is in effect + * from the very first tick. An explicit --rt/--no-rt on the command line + * still overrides this if the user wants the old fast-as-possible behaviour + * (those options are parsed before PRE_BOOT_2... so to let --no-rt win we + * would need PRE_BOOT_1; PRE_BOOT_2 intentionally forces real-time). */ +NATIVE_TASK(zephcore_native_force_realtime, PRE_BOOT_2, 1); diff --git a/zephcore/boards/common/linux_common.overlay b/zephcore/boards/common/linux_common.overlay index 3761760..32efba4 100644 --- a/zephcore/boards/common/linux_common.overlay +++ b/zephcore/boards/common/linux_common.overlay @@ -109,7 +109,7 @@ rx-enable-gpios = <&zephcore_gpio0 24 GPIO_ACTIVE_HIGH>; dio2-tx-enable; dio3-tcxo-voltage = ; - tcxo-power-startup-delay-ms = <10>; + tcxo-power-startup-delay-ms = <5>; }; }; }; diff --git a/zephcore/patches/zephyr-new/drivers/gpio/gpio_native_linux.c b/zephcore/patches/zephyr-new/drivers/gpio/gpio_native_linux.c index 2792077..af10c39 100644 --- a/zephcore/patches/zephyr-new/drivers/gpio/gpio_native_linux.c +++ b/zephcore/patches/zephyr-new/drivers/gpio/gpio_native_linux.c @@ -167,9 +167,6 @@ static int gnl_pin_configure(const struct device *dev, gpio_pin_t pin, k_mutex_lock(&data->mu, K_FOREVER); data->pins[pin].cfg_flags = flags; ret = rebuild_line(dev, pin); - LOG_WRN("pin_configure: dev=%p data=%p pin=%u flags=0x%x ret=%d requested=%d", - (void *)dev, (void *)data, pin, flags, ret, - data->pins[pin].requested); k_mutex_unlock(&data->mu); /* Wake the event thread to re-collect fds. */ @@ -184,9 +181,6 @@ static int gnl_port_get_raw(const struct device *dev, gpio_port_value_t *value) struct gpio_native_linux_data *data = dev->data; gpio_port_value_t v = 0; - static int dbgn; - bool dbg = dbgn < 20; - k_mutex_lock(&data->mu, K_FOREVER); for (uint32_t i = 0; i < cfg->ngpios && i < GNL_MAX_PINS; i++) { struct gnl_pin_state *p = &data->pins[i]; @@ -196,21 +190,12 @@ static int gnl_port_get_raw(const struct device *dev, gpio_port_value_t *value) } int x = gnl_line_get_value(p->line); - if (dbg) { - LOG_WRN("port_get_raw: pin %u requested, value=%d", i, x); - } if (x > 0) { v |= ((gpio_port_value_t)1U << i); } } k_mutex_unlock(&data->mu); - if (dbg) { - LOG_WRN("port_get_raw: dev=%p data=%p portval=0x%08x", - (void *)dev, (void *)data, (uint32_t)v); - dbgn++; - } - *value = v; return 0; } diff --git a/zephcore/patches/zephyr-new/drivers/gpio/gpio_native_linux_adapt.c b/zephcore/patches/zephyr-new/drivers/gpio/gpio_native_linux_adapt.c index cbd92ea..61b58b6 100644 --- a/zephcore/patches/zephyr-new/drivers/gpio/gpio_native_linux_adapt.c +++ b/zephcore/patches/zephyr-new/drivers/gpio/gpio_native_linux_adapt.c @@ -231,29 +231,7 @@ int gnl_line_set_value(gnl_line_t line, int value) vals.mask = 1ULL; vals.bits = value ? 1ULL : 0ULL; - int rc = ioctl(h->line_fd, GPIO_V2_LINE_SET_VALUES_IOCTL, &vals); - - /* DIAGNOSTIC (temporary): confirm output writes reach the kernel and - * read the line back immediately to see if the drive physically took. */ - { - static int dbgn; - - if (dbgn < 40) { - dbgn++; - struct gpio_v2_line_values rb; - - memset(&rb, 0, sizeof(rb)); - rb.mask = 1ULL; - int grc = ioctl(h->line_fd, GPIO_V2_LINE_GET_VALUES_IOCTL, &rb); - - fprintf(stderr, - "GNL set_value: off=%u fd=%d val=%d set_rc=%d errno=%d readback=%d (grc=%d)\n", - h->offset, h->line_fd, value, rc, rc < 0 ? errno : 0, - (grc == 0) ? (int)(rb.bits & 1ULL) : -1, grc); - } - } - - if (rc < 0) { + if (ioctl(h->line_fd, GPIO_V2_LINE_SET_VALUES_IOCTL, &vals) < 0) { return -errno; } return 0; diff --git a/zephcore/patches/zephyr/0008-flash-sim-per-node-file.patch b/zephcore/patches/zephyr/0008-flash-sim-per-node-file.patch new file mode 100644 index 0000000..20bbacc --- /dev/null +++ b/zephcore/patches/zephyr/0008-flash-sim-per-node-file.patch @@ -0,0 +1,67 @@ +diff --git a/drivers/flash/flash_simulator.c b/drivers/flash/flash_simulator.c +index bbc1e6e5af7..c13b0285dd1 100644 +--- a/drivers/flash/flash_simulator.c ++++ b/drivers/flash/flash_simulator.c +@@ -438,7 +438,11 @@ static int flash_mock_init(const struct device *dev) + struct flash_simulator_data *dev_data = dev->data; + + if (dev_data->flash_in_ram == false && dev_data->flash_file_path == NULL) { +- dev_data->flash_file_path = cfg->flash_file_default_path; ++ /* ZephCore: default to a per-node file (meshcore_settings_.bin) ++ * so persistent settings are identifiable and survive restarts. ++ * cfg->flash_file_default_path ("flash.bin") is the upstream default; ++ * an explicit --flash= still overrides (flash_file_path != NULL). */ ++ dev_data->flash_file_path = flash_mock_default_path_native(); + } + + rc = flash_mock_init_native(dev_data->flash_in_ram, &dev_data->mock_flash, cfg->flash_size, +diff --git a/drivers/flash/flash_simulator_native.c b/drivers/flash/flash_simulator_native.c +index 03eb8269f58..73249b4fcdd 100644 +--- a/drivers/flash/flash_simulator_native.c ++++ b/drivers/flash/flash_simulator_native.c +@@ -28,6 +28,31 @@ + #include + #include + ++/* ++ * ZephCore: build a per-node default flash backing-file name from the host ++ * name, so each node keeps its persistent settings in its own file ++ * (meshcore_settings_.bin) rather than a generic flash.bin. ++ * Returns a pointer to a static buffer valid for the process lifetime. ++ */ ++const char *flash_mock_default_path_native(void) ++{ ++ static char path[128]; ++ char host[64]; ++ ++ if (gethostname(host, sizeof(host)) != 0) { ++ host[0] = '\0'; ++ } ++ host[sizeof(host) - 1] = '\0'; ++ ++ if (host[0] == '\0') { ++ snprintf(path, sizeof(path), "meshcore_settings.bin"); ++ } else { ++ snprintf(path, sizeof(path), "meshcore_settings_%s.bin", host); ++ } ++ ++ return path; ++} ++ + /* + * Initialize the flash buffer. + * And, if the content is to be kept on disk map it to the buffer to the file. +diff --git a/drivers/flash/flash_simulator_native.h b/drivers/flash/flash_simulator_native.h +index a59136c1c20..efc69b7366e 100644 +--- a/drivers/flash/flash_simulator_native.h ++++ b/drivers/flash/flash_simulator_native.h +@@ -15,6 +15,9 @@ int flash_mock_init_native(bool flash_in_ram, uint8_t **mock_flash, unsigned int + int *flash_fd, const char *flash_file_path, + unsigned int erase_value, bool flash_erase_at_start); + ++/* ZephCore: per-node default backing-file path (meshcore_settings_.bin). */ ++const char *flash_mock_default_path_native(void); ++ + void flash_mock_cleanup_native(bool flash_in_ram, int flash_fd, uint8_t *mock_flash, + unsigned int size, const char *flash_file_path, + bool flash_rm_at_exit);