23#include "hardware/watchdog.h"
25#include "pico/types.h"
42#define MAIN_LOOP_SLEEP_MS 1
43#define MAIN_LOOP_SLEEP_US 50
45#define TX_QUEUE_DRAIN_CHUNK_SIZE 32U
46#define TX_QUEUE_HIGH_WM 48U
47#define TX_QUEUE_LOW_WM 12U
49#define EVENT_LOOP_MAX_EVENTS_AT_ONCE 20
50#define EVENT_LOOP_MAX_EVENTS_WHILE_TX_ACTIVE 2
51#define HDLC_DECODE_FAIL_STREAK_LIMIT 4U
52#define HDLC_SYNC_IDLE_TIMEOUT_US 20000U
53#define HDLC_SYNC_NO_PROGRESS_MAX_BYTES 2048U
54#define HDLC_SYNC_NO_PROGRESS_MAX_BYTES_EXTERNAL 12288
66 uint32_t* idle_run_length)
68 if (!app || !idle_run_length)
73 bool in_external_hunting =
75 if (!in_external_hunting)
77 *idle_run_length = 0U;
81 if (rx_byte == 0x00U || rx_byte == 0xFFU)
83 if (*idle_run_length >= 2U)
91 *idle_run_length = 0U;
126 const size_t rx_drained_early,
const uint64_t now_us)
134 if (rx_drained_early > 0U)
148 if (enqueue_result ==
E2S_OK)
159 LOG_ERROR(
"TX Queue Enqueue failed: %d.\r\n", enqueue_result);
169 size_t bytes_drained = 0;
172 LOG_ERROR(
"Poll Queue Stats failed.\r\n");
178 LOG_ERROR(
"Poll Queue Drain failed.\r\n");
180 else if (bytes_drained > 0U)
191 return bytes_drained;
195 const uint64_t now_us)
204 LOG_DEBUG(
"HDLC idle timeout resync\r\n");
232 LOG_DEBUG(
"HDLC first decode fail: off=%u shift_right=%u cand_start=%zu "
233 "cand_end=%zu pos=%zu proc=%zu\r\n",
247 LOG_DEBUG(
"HDLC hard resync after %u decode fails\r\n",
282 if (!decode_in_progress)
293 if (have_decode_lock && decode_in_progress &&
296 no_progress_max_bytes))
301 no_progress_max_bytes);
351 int max_events_per_iteration =
355 for (
int i = 0; i < max_events_per_iteration &&
event_queue_pop(&event_item); i++)
386 .hdlc_decode_fail_streak = 0U,
387 .last_rx_byte_us = 0U,
388 .last_frame_ready_bytes = 0U,
389 .hunt_idle_run_length = 0U,
391 .udp_rx_throttled =
false};
401 size_t rx_drained_early = 0;
404 to_us_since_boot(get_absolute_time()));
421 size_t rx_drained = 0;
423 uint64_t now_us = to_us_since_boot(get_absolute_time());
void cli_poll(void)
Poll USB CDC for CLI input, echo, and emit line events.
bool log_take_emitted_flag(void)
Atomically read and clear "log emitted" flag.
uint32_t log_take_dropped_count(void)
uint32_t log_get_high_water_mark(void)
e2s_error_t
Common error codes returned by ether2ser modules.
@ E2S_ERR_HDLC_ACC_FRAME_READY
void event_dispatch(const event_t *event, app_ctx_t *app)
Dispatch one event to the corresponding handler.
#define HDLC_SYNC_NO_PROGRESS_MAX_BYTES_EXTERNAL
static bool event_loop_should_drop_hunt_idle_byte(app_ctx_t *app, uint8_t rx_byte, uint32_t *idle_run_length)
#define MAIN_LOOP_SLEEP_US
static void update_rx_drain_stats(app_ctx_t *app, event_loop_runtime_t *event_loop_runtime, const size_t rx_drained_early, const uint64_t now_us)
static void print_prompt(app_ctx_t *app)
#define HDLC_SYNC_IDLE_TIMEOUT_US
static void drain_rx_until_empty(app_ctx_t *app, event_loop_runtime_t *runtime, size_t *rx_drained)
static void poll_hdlc_no_progress(app_ctx_t *app, event_loop_runtime_t *event_loop_runtime)
static void poll_and_dispatch_events(app_ctx_t *app)
#define HDLC_SYNC_NO_PROGRESS_MAX_BYTES
static void poll_hdlc_idle_timeout(app_ctx_t *app, event_loop_runtime_t *event_loop_runtime, const uint64_t now_us)
static void poll_and_enqueue_udp_rx(app_ctx_t *app, event_loop_runtime_t *event_loop_runtime)
static void update_udp_rx_throttle_state(app_ctx_t *app, event_loop_runtime_t *runtime)
static void drain_hdlc_frames_to_udp(app_ctx_t *app, event_loop_runtime_t *event_loop_runtime)
#define TX_QUEUE_DRAIN_CHUNK_SIZE
#define EVENT_LOOP_MAX_EVENTS_AT_ONCE
static size_t poll_tx_pipeline(app_ctx_t *app, event_loop_runtime_t *event_loop_runtime)
#define EVENT_LOOP_MAX_EVENTS_WHILE_TX_ACTIVE
#define HDLC_DECODE_FAIL_STREAK_LIMIT
void event_loop(app_ctx_t *app)
Run the main application polling loop.
static void update_statistics(app_ctx_t *app)
static void decode_hdlc_to_udp_tx(app_ctx_t *app, event_loop_runtime_t *event_loop_runtime)
uint32_t event_queue_get_push_drop_count(void)
Get cumulative number of dropped push attempts.
bool event_queue_pop(event_t *event_out)
Dequeue an event.
size_t event_queue_get_count(void)
Get current number of queued events.
size_t event_queue_get_high_water_mark(void)
Get peak queue fill count observed since init.
bool hdlc_decode(const HDLC_FRAME_T *frame, uint8_t *payload, const size_t out_capacity, size_t *payload_length, bool lsb_first)
Decode an HDLC bit-stuffed frame.
void hdlc_decode_stats_snapshot(hdlc_decode_stats_t *out_stats)
Copy cumulative HDLC decode statistics.
void hdlc_sync_acc_consume_candidate(HDLC_SYNC_ACCUMULATOR_T *accumulator, bool accept)
Consume current candidate and advance accumulator window.
void hdlc_sync_acc_init(HDLC_SYNC_ACCUMULATOR_T *accumulator, uint8_t sync_byte)
Initialize HDLC sync accumulator state.
e2s_error_t hdlc_sync_acc_poll(HDLC_SYNC_ACCUMULATOR_T *accumulator, HDLC_FRAME_T *out_frame)
Poll accumulator for an aligned HDLC frame candidate.
bool hdlc_sync_acc_process_byte(HDLC_SYNC_ACCUMULATOR_T *accumulator, uint8_t byte)
Append one received raw byte to the accumulator.
@ HDLC_SYNC_STATE_HUNTING
bool tx_poll(void)
Poll TX completion/holdoff state and manage RTS release.
bool rx_get(uint8_t *data)
Read one received byte from RX PIO FIFO.
bool rx_clock_poll_stall(void)
Poll RX FIFO stall events and update the provided event counter.
void rx_clock_hard_reset(void)
Disable RX Path, clear fifos, restart SMs and CLKDIV and enable again.
uint32_t lookahead_wait_synced
uint32_t hardcap_drop_bytes
uint32_t lookahead_wait_syncing
uint32_t hardcap_drop_events
Global application context shared across modules.
UDP_FRAME_T tx_frame_buffer
UDP_FRAME_T rx_frame_buffer
HDLC_SYNC_ACCUMULATOR_T accumulator
payload_statistics_t stats
UDP_CONFIG_T sender_config
UDP_CONFIG_T destination_config
HDLC_FRAME_T reconstructed_frame
uint8_t hdlc_decode_fail_streak
uint64_t last_frame_ready_bytes
uint32_t hunt_idle_run_length
Cumulative HDLC decode failure reason counters.
uint64_t payload_too_long
uint64_t rx_fifo_stall_events
uint64_t sync_candidate_consume
uint64_t hdlc_decode_fail
uint64_t tx_queue_used_max
uint64_t resync_no_progress_count
uint64_t decode_fail_crc_mismatch
uint64_t sync_hardcap_drop_events
uint64_t udp_rx_throttle_enter
uint64_t tx_queue_drop_frames
uint64_t accumulator_pos_max
uint64_t hunt_idle_drop_bytes
uint64_t event_queue_drop_events
uint64_t decode_fail_unstuff_error
uint64_t udp_rx_throttle_skips
uint64_t log_queue_used_max
uint64_t hdlc_frame_ready
uint64_t serial_rx_drop_acc_full
uint64_t decode_fail_invalid_frame
uint64_t event_queue_used_max
uint64_t decode_fail_too_short
uint64_t sync_hardcap_drop_bytes
uint64_t resync_idle_timeout_count
uint64_t sync_lookahead_wait_syncing
uint64_t resync_hard_fail_count
uint64_t udp_tx_buffer_full_counts
uint64_t decode_fail_payload_too_long
uint64_t sync_lookahead_wait_synced
uint64_t udp_rx_buffer_full_counts
e2s_error_t poll_queue_stats(TX_QUEUE_T *queue)
Emit queue usage statistics when needed.
bool tx_queue_is_empty(TX_QUEUE_T *queue)
Check whether queue and active entry are fully drained.
size_t tx_queue_get_count(const TX_QUEUE_T *queue)
Get current number of queued entries.
e2s_error_t tx_queue_enqueue_udp_frame(TX_QUEUE_T *queue, const UDP_FRAME_T *frame)
Encode UDP frame to HDLC and append it to TX queue.
e2s_error_t tx_queue_drain(TX_QUEUE_T *queue, size_t bytes_to_drain, size_t *bytes_drained)
Drain up to bytes_to_drain bytes from queue into TX FIFO.
void w5500_poll_udp_buffer_full_events(uint64_t *rx_full_enter_events, uint64_t *tx_full_enter_events)
void w5500_udp_tx(UDP_CONFIG_T *send_config, const UDP_FRAME_T *frame)
Send one UDP frame through W5500.
bool w5500_poll_rx(UDP_CONFIG_T *send_config, UDP_FRAME_T *frame)
Poll W5500 for received UDP data.