15#if PC_ENABLE_PREEMPT_QUEUE
20#include "freertos/FreeRTOS.h"
21#include "freertos/queue.h"
22#include "freertos/task.h"
32 pc_pq_handler handler[(size_t)pc_pq_lane::PC_PQ_LANE_COUNT] = {};
33 void *ctx[(size_t)pc_pq_lane::PC_PQ_LANE_COUNT] = {};
34 size_t high_water[(size_t)pc_pq_lane::PC_PQ_LANE_COUNT] = {};
40const char *lane_name(pc_pq_lane lane)
44 case pc_pq_lane::PC_PQ_LANE_DMA:
46 case pc_pq_lane::PC_PQ_LANE_FORWARD:
48 case pc_pq_lane::PC_PQ_LANE_DEVICE:
56bool lane_ok(pc_pq_lane lane)
58 return (
unsigned)lane < (unsigned)pc_pq_lane::PC_PQ_LANE_COUNT;
64uint8_t pc_pq_lane_priority(pc_pq_lane lane)
68 case pc_pq_lane::PC_PQ_LANE_DMA:
70 case pc_pq_lane::PC_PQ_LANE_FORWARD:
72 case pc_pq_lane::PC_PQ_LANE_DEVICE:
74 case pc_pq_lane::PC_PQ_LANE_USER:
89 StaticQueue_t q_struct[(size_t)pc_pq_lane::PC_PQ_LANE_COUNT];
91 QueueHandle_t q[(size_t)pc_pq_lane::PC_PQ_LANE_COUNT] = {};
92 TaskHandle_t task[(size_t)pc_pq_lane::PC_PQ_LANE_COUNT] = {};
93 volatile bool run[(size_t)pc_pq_lane::PC_PQ_LANE_COUNT] = {};
97void note_depth(pc_pq_lane lane, UBaseType_t waiting)
99 if ((
size_t)waiting > s_pq.high_water[(
size_t)lane])
101 s_pq.high_water[(size_t)lane] = (
size_t)waiting;
108void pq_task(
void *arg)
110 pc_pq_lane lane =
static_cast<pc_pq_lane
>(
reinterpret_cast<uintptr_t
>(arg));
114 if (xQueueReceive(s_pqq.q[(
size_t)lane], item, portMAX_DELAY) == pdTRUE && s_pq.handler[(
size_t)lane])
116 s_pq.handler[(size_t)lane](item, s_pq.ctx[(
size_t)lane]);
122bool pc_pq_start_lane(pc_pq_lane lane,
const pc_pq_config *cfg)
124 if (!lane_ok(lane) || s_pqq.run[(
size_t)lane] || !cfg || !cfg->handler)
128 s_pq.handler[(size_t)lane] = cfg->handler;
129 s_pq.ctx[(size_t)lane] = cfg->ctx;
130 s_pq.high_water[(size_t)lane] = 0;
131 if (!s_pqq.q[(
size_t)lane])
134 &s_pqq.q_struct[(size_t)lane]);
136 if (!s_pqq.q[(
size_t)lane])
140 s_pqq.run[(size_t)lane] =
true;
141 uint8_t prio = cfg->priority ? cfg->priority : pc_pq_lane_priority(lane);
142 int core = cfg->core % portNUM_PROCESSORS;
143 if (xTaskCreatePinnedToCore(pq_task, cfg->name ? cfg->name : lane_name(lane),
PC_PQ_STACK, (void *)(uintptr_t)lane,
144 prio, &s_pqq.task[(size_t)lane], core) != pdPASS)
146 s_pqq.run[(size_t)lane] =
false;
152bool pc_pq_post_lane(pc_pq_lane lane,
const void *item, uint32_t timeout_ticks)
154 if (!lane_ok(lane) || !s_pqq.q[(
size_t)lane] || !item)
158 if (xQueueSendToBack(s_pqq.q[(
size_t)lane], item, (TickType_t)timeout_ticks) != pdTRUE)
162 note_depth(lane, uxQueueMessagesWaiting(s_pqq.q[(
size_t)lane]));
166bool pc_pq_post_lane_urgent(pc_pq_lane lane,
const void *item, uint32_t timeout_ticks)
168 if (!lane_ok(lane) || !s_pqq.q[(
size_t)lane] || !item)
172 if (xQueueSendToFront(s_pqq.q[(
size_t)lane], item, (TickType_t)timeout_ticks) != pdTRUE)
176 note_depth(lane, uxQueueMessagesWaiting(s_pqq.q[(
size_t)lane]));
180bool pc_pq_post_lane_from_isr(pc_pq_lane lane,
const void *item)
182 if (!lane_ok(lane) || !s_pqq.q[(
size_t)lane] || !item)
186 BaseType_t woke = pdFALSE;
187 if (xQueueSendToBackFromISR(s_pqq.q[(
size_t)lane], item, &woke) != pdTRUE)
191 note_depth(lane, uxQueueMessagesWaitingFromISR(s_pqq.q[(
size_t)lane]));
192 portYIELD_FROM_ISR(woke);
196void pc_pq_drain_lane(pc_pq_lane)
201void pc_pq_stop_lane(pc_pq_lane lane)
207 s_pqq.run[(size_t)lane] =
false;
208 if (s_pqq.task[(
size_t)lane])
210 vTaskDelete(s_pqq.task[(
size_t)lane]);
211 s_pqq.task[(size_t)lane] =
nullptr;
215bool pc_pq_running_lane(pc_pq_lane lane)
217 return lane_ok(lane) && s_pqq.run[(size_t)lane];
220size_t pc_pq_high_water_lane(pc_pq_lane lane)
222 return lane_ok(lane) ? s_pq.high_water[(size_t)lane] : 0;
234 size_t head[(size_t)pc_pq_lane::PC_PQ_LANE_COUNT] = {};
235 size_t tail[(size_t)pc_pq_lane::PC_PQ_LANE_COUNT] = {};
236 size_t count[(size_t)pc_pq_lane::PC_PQ_LANE_COUNT] = {};
237 bool started[(size_t)pc_pq_lane::PC_PQ_LANE_COUNT] = {};
241void note_count(pc_pq_lane lane)
243 if (s_pqr.count[(
size_t)lane] > s_pq.high_water[(
size_t)lane])
245 s_pq.high_water[(size_t)lane] = s_pqr.count[(
size_t)lane];
250bool pc_pq_start_lane(pc_pq_lane lane,
const pc_pq_config *cfg)
252 if (!lane_ok(lane) || s_pqr.started[(
size_t)lane] || !cfg || !cfg->handler)
256 s_pq.handler[(size_t)lane] = cfg->handler;
257 s_pq.ctx[(size_t)lane] = cfg->ctx;
258 s_pqr.head[(size_t)lane] = 0;
259 s_pqr.tail[(size_t)lane] = 0;
260 s_pqr.count[(size_t)lane] = 0;
261 s_pq.high_water[(size_t)lane] = 0;
262 s_pqr.started[(size_t)lane] =
true;
266bool pc_pq_post_lane(pc_pq_lane lane,
const void *item, uint32_t)
268 if (!lane_ok(lane) || !item || s_pqr.count[(
size_t)lane] >=
PC_PQ_DEPTH)
273 s_pqr.head[(size_t)lane] = (s_pqr.head[(
size_t)lane] + 1) %
PC_PQ_DEPTH;
274 s_pqr.count[(size_t)lane]++;
279bool pc_pq_post_lane_urgent(pc_pq_lane lane,
const void *item, uint32_t)
281 if (!lane_ok(lane) || !item || s_pqr.count[(
size_t)lane] >=
PC_PQ_DEPTH)
285 s_pqr.tail[(size_t)lane] =
288 s_pqr.count[(size_t)lane]++;
293bool pc_pq_post_lane_from_isr(pc_pq_lane lane,
const void *item)
295 return pc_pq_post_lane(lane, item, 0);
298void pc_pq_drain_lane(pc_pq_lane lane)
304 while (s_pqr.count[(
size_t)lane] > 0)
306 if (s_pq.handler[(
size_t)lane])
308 s_pq.handler[(size_t)lane](s_pqr.buf[(
size_t)lane] + s_pqr.tail[(size_t)lane] *
PC_PQ_ITEM_SIZE,
309 s_pq.ctx[(
size_t)lane]);
311 s_pqr.tail[(size_t)lane] = (s_pqr.tail[(
size_t)lane] + 1) %
PC_PQ_DEPTH;
312 s_pqr.count[(size_t)lane]--;
316void pc_pq_stop_lane(pc_pq_lane lane)
320 s_pqr.started[(size_t)lane] =
false;
324bool pc_pq_running_lane(pc_pq_lane lane)
326 return lane_ok(lane) && s_pqr.started[(size_t)lane];
329size_t pc_pq_high_water_lane(pc_pq_lane lane)
331 return lane_ok(lane) ? s_pq.high_water[(size_t)lane] : 0;
User-configurable preempting work queues + high-priority processing tasks (PC_ENABLE_PREEMPT_QUEUE) -...
#define PC_PQ_ITEM_SIZE
Bytes per preempting-queue item (the posted item must fit).
#define PC_PQ_INTERNAL_PRIORITY
Base FreeRTOS priority for the internal preempting lanes (DMA / forwarding / device access)....
#define PC_PQ_DEPTH
Capacity of the preempting queue in items (static-allocated).
#define PC_PQ_STACK
Stack (bytes) for each preempting-queue processing task (ESP32).