Merge remote-tracking branch 'upstream/feed-wdt-inline-fastpath' into integration

This commit is contained in:
J. Nick Koston
2026-04-11 15:05:52 -10:00
5 changed files with 66 additions and 29 deletions
+26 -17
View File
@@ -106,6 +106,30 @@ void IRAM_ATTR CC1101Component::gpio_intr(CC1101Component *arg) { arg->enable_lo
void CC1101Component::setup() {
this->spi_setup();
if (this->gdo0_pin_ != nullptr) {
this->gdo0_pin_->setup();
}
this->configure();
if (this->is_failed()) {
return;
}
// Defer pin mode setup until after all components have completed setup()
// This handles the case where remote_transmitter runs after CC1101 and changes pin mode
if (this->gdo0_pin_ != nullptr) {
this->defer([this]() {
this->gdo0_pin_->pin_mode(gpio::FLAG_INPUT);
if (this->state_.PKT_FORMAT == static_cast<uint8_t>(PacketFormat::PACKET_FORMAT_FIFO)) {
this->gdo0_pin_->attach_interrupt(&CC1101Component::gpio_intr, this, gpio::INTERRUPT_RISING_EDGE);
}
});
}
}
void CC1101Component::configure() {
// Manual reset sequence per CC1101 datasheet section 19.1.2
this->cs_->digital_write(true);
delayMicroseconds(1);
this->cs_->digital_write(false);
@@ -128,11 +152,6 @@ void CC1101Component::setup() {
return;
}
// Setup GDO0 pin if configured
if (this->gdo0_pin_ != nullptr) {
this->gdo0_pin_->setup();
}
this->initialized_ = true;
for (uint8_t i = 0; i <= static_cast<uint8_t>(Register::TEST0); i++) {
@@ -142,21 +161,11 @@ void CC1101Component::setup() {
this->write_(static_cast<Register>(i));
}
this->set_output_power(this->output_power_requested_);
if (!this->enter_rx_()) {
this->mark_failed();
return;
}
// Defer pin mode setup until after all components have completed setup()
// This handles the case where remote_transmitter runs after CC1101 and changes pin mode
if (this->gdo0_pin_ != nullptr) {
this->defer([this]() {
this->gdo0_pin_->pin_mode(gpio::FLAG_INPUT);
if (this->state_.PKT_FORMAT == static_cast<uint8_t>(PacketFormat::PACKET_FORMAT_FIFO)) {
this->gdo0_pin_->attach_interrupt(&CC1101Component::gpio_intr, this, gpio::INTERRUPT_RISING_EDGE);
}
});
}
}
void CC1101Component::call_listeners_(const std::vector<uint8_t> &packet, float freq_offset, float rssi, uint8_t lqi) {
@@ -273,7 +282,7 @@ void CC1101Component::begin_rx() {
void CC1101Component::reset() {
this->strobe_(Command::RES);
this->setup();
this->configure();
}
void CC1101Component::set_idle() {
+1
View File
@@ -25,6 +25,7 @@ class CC1101Component : public Component,
void setup() override;
void loop() override;
void dump_config() override;
void configure();
// Actions
void begin_tx();
+5 -5
View File
@@ -196,14 +196,14 @@ void Application::process_dump_config_() {
this->dump_config_at_++;
}
void HOT Application::feed_wdt(uint32_t time) {
static uint32_t last_feed = 0;
void HOT Application::feed_wdt_slow_(uint32_t time) {
// Use provided time if available, otherwise get current time
uint32_t now = time ? time : millis();
// Compare in milliseconds (3ms threshold)
if (now - last_feed > 3) {
// The inline wrapper already performs this check when time != 0;
// repeat it here for the time == 0 entry and as a safety net.
if (now - this->last_wdt_feed_ > WDT_FEED_INTERVAL_MS) {
arch_feed_wdt();
last_feed = now;
this->last_wdt_feed_ = now;
#ifdef USE_STATUS_LED
if (status_led::global_status_led != nullptr) {
auto *sl = status_led::global_status_led;
+27 -6
View File
@@ -229,7 +229,23 @@ class Application {
void schedule_dump_config() { this->dump_config_at_ = 0; }
void feed_wdt(uint32_t time = 0);
/// Minimum interval between real arch_feed_wdt() calls. Chosen to keep the
/// rate of HAL pokes low while still being small enough that any plausible
/// watchdog timeout (seconds) has orders of magnitude of safety margin.
static constexpr uint32_t WDT_FEED_INTERVAL_MS = 3;
/// Feed the task watchdog. Hot-path inline rate-limit check: callers that
/// already have a timestamp in hand pay only a load + sub + branch on the
/// common (no-op) path. The actual arch feed + status LED update live in
/// feed_wdt_slow_ to keep this small enough to inline freely.
///
/// Pass time==0 to request millis() be read for you (low-frequency callers
/// only — always takes the slow path).
void ESPHOME_ALWAYS_INLINE feed_wdt(uint32_t time = 0) {
if (time == 0 || static_cast<uint32_t>(time - this->last_wdt_feed_) > WDT_FEED_INTERVAL_MS) {
this->feed_wdt_slow_(time);
}
}
void reboot();
@@ -370,7 +386,10 @@ class Application {
/// Caller must ensure dump_config_at_ < components_.size().
void __attribute__((noinline)) process_dump_config_();
void feed_wdt_arch_();
/// Slow path for feed_wdt(): actually calls arch_feed_wdt(), updates
/// last_wdt_feed_, and re-dispatches the status LED. Out of line so the
/// inline wrapper stays tiny.
void feed_wdt_slow_(uint32_t time);
/// Perform a delay while also monitoring socket file descriptors for readiness
#ifdef USE_HOST
@@ -422,6 +441,7 @@ class Application {
// 4-byte members
uint32_t last_loop_{0};
uint32_t loop_component_start_time_{0};
uint32_t last_wdt_feed_{0}; // millis() of most recent arch_feed_wdt(); rate-limits feed_wdt() hot path
#ifdef USE_HOST
int max_fd_{-1}; // Highest file descriptor number for select()
@@ -506,12 +526,13 @@ inline void ESPHOME_ALWAYS_INLINE Application::before_loop_tasks_(uint32_t loop_
this->drain_wake_notifications_();
#endif
// Process scheduled tasks
// Process scheduled tasks. Scheduler::call now feeds the watchdog itself
// after each scheduled item that actually runs, so we no longer need an
// unconditional feed here — when Scheduler::call has no work to do, the
// only elapsed time is a sleep wake + a few instructions, and when it does
// have work, it fed the wdt as it went.
this->scheduler.call(loop_start_time);
// Feed the watchdog timer
this->feed_wdt(loop_start_time);
// Process any pending enable_loop requests from ISRs
// This must be done before marking in_loop_ = true to avoid race conditions
if (this->has_pending_enable_loop_requests_) {
+7 -1
View File
@@ -739,7 +739,13 @@ uint32_t HOT Scheduler::execute_item_(SchedulerItem *item, uint32_t now) {
App.set_current_component(item->component);
WarnIfComponentBlockingGuard guard{item->component, now};
item->callback();
return guard.finish();
uint32_t end = guard.finish();
// Feed the watchdog after each scheduled item (both main heap and defer
// queue paths go through here). A run of back-to-back callbacks cannot
// starve the wdt. The inline fast path is a load + sub + branch — nearly
// free when the 3 ms rate limit hasn't elapsed.
App.feed_wdt(end);
return end;
}
// Common implementation for cancel operations - handles locking