From 7b1e098097f147f2f8917ffe4dc4d797423a0e02 Mon Sep 17 00:00:00 2001 From: Geovane Fedrecheski Date: Sun, 5 Jan 2025 18:10:10 +0100 Subject: [PATCH 01/17] tsch: add basic scheduler and skeleton for tsch mac --- drv/drv.emProject | 18 +++ drv/scheduler.h | 102 +++++++++++++++ drv/scheduler/all_schedules.c | 70 ++++++++++ drv/scheduler/scheduler.c | 141 +++++++++++++++++++++ drv/tsch.h | 58 +++++++++ drv/tsch/tsch.c | 56 ++++++++ projects/01drv_scheduler/01drv_scheduler.c | 60 +++++++++ projects/01drv_scheduler/README.md | 1 + projects/01drv_tsch/01drv_tsch.c | 29 +++++ projects/01drv_tsch/README.md | 1 + projects/projects-bsp-drv.emProject | 50 ++++++++ 11 files changed, 586 insertions(+) create mode 100644 drv/scheduler.h create mode 100644 drv/scheduler/all_schedules.c create mode 100644 drv/scheduler/scheduler.c create mode 100644 drv/tsch.h create mode 100644 drv/tsch/tsch.c create mode 100644 projects/01drv_scheduler/01drv_scheduler.c create mode 100644 projects/01drv_scheduler/README.md create mode 100644 projects/01drv_tsch/01drv_tsch.c create mode 100644 projects/01drv_tsch/README.md diff --git a/drv/drv.emProject b/drv/drv.emProject index 3f450ceb3..e2b59ba00 100644 --- a/drv/drv.emProject +++ b/drv/drv.emProject @@ -120,6 +120,24 @@ + + + + + + + + + + + * @copyright Inria, 2024-now + * @} + */ + +#include +#include +#include +#include + +#include "radio.h" +#include "tsch.h" +#include "protocol.h" +#include "gpio.h" + +//=========================== defines ========================================== + +#define TSCH_BACKOFF_N_MIN 5 +#define TSCH_BACKOFF_N_MAX 9 + +#define TSCH_N_BLE_REGULAR_FREQUENCIES 37 +#define TSCH_N_BLE_ADVERTISING_FREQUENCIES 3 + +#define TSCH_N_CELLS_MAX 101 + +//=========================== variables ======================================== + +typedef enum { + SLOT_TYPE_BEACON = 'B', + SLOT_TYPE_SHARED_UPLINK = 'S', + SLOT_TYPE_DOWNLINK = 'D', + SLOT_TYPE_UPLINK = 'U', +} slot_type_t; + +typedef struct { + slot_type_t type; + uint8_t channel_offset; + uint64_t assigned_node_id; +} cell_t; + +typedef struct { + uint8_t id; // unique identifier for the schedule + uint8_t max_nodes; // maximum number of nodes that can be scheduled, equivalent to the number of uplink slots + uint8_t backoff_n_min; // minimum exponent for the backoff algorithm + uint8_t backoff_n_max; // maximum exponent for the backoff algorithm + uint16_t slot_duration_us; // duration of a slot in microseconds + size_t n_cells; // number of cells in this schedule + cell_t cells[TSCH_N_CELLS_MAX]; // cells in this schedule. NOTE(FIXME?): the first 3 cells must be beacons +} schedule_t; + +//=========================== prototypes ========================================== + +/** + * @brief Initializes the scheduler + * + * If a schedule is not set, the first schedule in the available_schedules array is used. + * + * @param[in] schedule Schedule to be used. + */ +void db_scheduler_init(schedule_t *schedule); + +/** + * @brief Advances the schedule by one cell/slot. + * + * @return A configuration for the TSCH radio driver to follow in the next slot. + */ +tsch_radio_event_t db_scheduler_tick(void); + +/** + * @brief Activates a given schedule. + * + * This can be used at runtime to change the schedule, for example after receiving a beacon with a different schedule id. + * + * @param[in] schedule_id Schedule ID + * + * @return true if the schedule was successfully set, false otherwise + */ +bool db_scheduler_set_schedule(uint8_t schedule_id); + +/** + * @brief Computes the frequency to be used in a given slot. + * + * @param[in] slot_type Type of slot + * @param[in] asn Absolute Slot Number + * @param[in] channel_offset Channel offset + * + * @return Frequency to be used in the given slot + * + */ +uint8_t db_scheduler_get_frequency(slot_type_t slot_type, uint64_t asn, uint8_t channel_offset); + +#endif diff --git a/drv/scheduler/all_schedules.c b/drv/scheduler/all_schedules.c new file mode 100644 index 000000000..e128932a0 --- /dev/null +++ b/drv/scheduler/all_schedules.c @@ -0,0 +1,70 @@ +/** + * @file + * @ingroup drv_tsch + * + * @brief Driver for Time-Slotted Channel Hopping (TSCH) + * + * @author Geovane Fedrecheski + * + * @copyright Inria, 2024 + */ +#include "scheduler.h" + +#define N_SCHEDULES 1 + 2 // account for the schedule that can be passed by the application during initialization + +/* Schedule with 11 slots, supporting up to 5 nodes */ +schedule_t schedule_minuscule = { + .id = 32, + .max_nodes = 5, + .backoff_n_min = 5, + .backoff_n_max = 9, + .slot_duration_us = 2024, + .n_cells = 11, + .cells = { + // Begin with beacon cells. They use their own channel offsets and frequencies. + {'B', 0, NULL}, + {'B', 1, NULL}, + {'B', 2, NULL}, + // Continue with regular cells. + {'S', 6, NULL}, + {'D', 3, NULL}, + {'U', 5, NULL}, + {'U', 1, NULL}, + {'D', 4, NULL}, + {'U', 0, NULL}, + {'U', 7, NULL}, + {'U', 2, NULL} + } +}; + +/* Schedule with 17 slots, supporting up to 11 nodes */ +schedule_t schedule_tiny = { + .id = 5, + .max_nodes = 11, + .backoff_n_min = 5, + .backoff_n_max = 9, + .slot_duration_us = 2024, + .n_cells = 17, + .cells = { + // Begin with beacon cells. They use their own channel offsets and frequencies. + {'B', 0, NULL}, + {'B', 1, NULL}, + {'B', 2, NULL}, + // Continue with regular cells. + {'S', 2, NULL}, + {'D', 5, NULL}, + {'U', 6, NULL}, + {'U', 13, NULL}, + {'U', 7, NULL}, + {'U', 0, NULL}, + {'D', 4, NULL}, + {'U', 10, NULL}, + {'U', 12, NULL}, + {'U', 1, NULL}, + {'U', 11, NULL}, + {'U', 8, NULL}, + {'U', 3, NULL}, + {'U', 9, NULL} + } +}; + diff --git a/drv/scheduler/scheduler.c b/drv/scheduler/scheduler.c new file mode 100644 index 000000000..88d0423f3 --- /dev/null +++ b/drv/scheduler/scheduler.c @@ -0,0 +1,141 @@ +/** + * @file + * @ingroup drv_tsch + * + * @brief Driver for Time-Slotted Channel Hopping (TSCH) + * + * @author Geovane Fedrecheski + * + * @copyright Inria, 2024 + */ +#include +#include +#include +#include +#include + +#include "scheduler.h" +#include "tsch.h" +#include "radio.h" +#include "timer_hf.h" +#include "protocol.h" +#include "device.h" +#if defined(NRF5340_XXAA) && defined(NRF_NETWORK) +#include "ipc.h" +#endif + +#include "all_schedules.c" + +//=========================== defines ========================================== + + +//=========================== variables ======================================== + +static const uint8_t _ble_chan_to_freq[40] = { + 4, 6, 8, + 10, 12, 14, 16, 18, + 20, 22, 24, 28, + 30, 32, 34, 36, 38, + 40, 42, 44, 46, 48, + 50, 52, 54, 56, 58, + 60, 62, 64, 66, 68, + 70, 72, 74, 76, 78, + 2, 26, 80 // Advertising channels +}; + +typedef struct { + // counters and indexes + uint64_t asn; // absolute slot number + + uint8_t active_schedule_id; // id of the active schedule in available_schedules + uint32_t slotframe_counter; // used to cycle beacon frequencies through slotframes (when listening for beacons at uplink slots) + size_t current_beacon_cell_index; + + // static data + schedule_t available_schedules[N_SCHEDULES]; + size_t available_schedules_len; +} schedule_vars_t; + +static schedule_vars_t _schedule_vars = { 0 }; + +//========================== prototypes ======================================== + + +//=========================== public =========================================== + +void db_scheduler_init(schedule_t *application_schedule) { + _schedule_vars.available_schedules[_schedule_vars.available_schedules_len++] = schedule_minuscule; + _schedule_vars.available_schedules[_schedule_vars.available_schedules_len++] = schedule_tiny; + if (application_schedule != NULL) { + _schedule_vars.available_schedules[_schedule_vars.available_schedules_len++] = *application_schedule; + _schedule_vars.active_schedule_id = application_schedule->id; + } +} + +bool db_scheduler_set_schedule(uint8_t schedule_id) { + for (size_t i = 0; i < N_SCHEDULES; i++) { + if (_schedule_vars.available_schedules[i].id == schedule_id) { + _schedule_vars.active_schedule_id = i; + return true; + } + } + return false; +} + +tsch_radio_event_t db_scheduler_tick(void) { + schedule_t active_schedule = _schedule_vars.available_schedules[_schedule_vars.active_schedule_id]; + + // increment ASN so that nodes are in sync + _schedule_vars.asn++; + + // advance the current cell + size_t cell_index = _schedule_vars.asn % active_schedule.n_cells; + cell_t current_cell = active_schedule.cells[cell_index]; + + // if wrapped, keep track of how many slotframes have passed (used to cycle beacon frequencies) + if (cell_index == 0) { + _schedule_vars.slotframe_counter++; + } + + tsch_radio_event_t radio_event = { + .radio_action = TSCH_RADIO_ACTION_SLEEP, + .duration_us = active_schedule.slot_duration_us, + .frequency = 0 + }; + + radio_event.frequency = db_scheduler_get_frequency(current_cell.type, _schedule_vars.asn, current_cell.channel_offset); + + if (current_cell.type == SLOT_TYPE_BEACON || current_cell.type == SLOT_TYPE_DOWNLINK) { + radio_event.radio_action = TSCH_RADIO_ACTION_RX; + } else if (current_cell.type == SLOT_TYPE_SHARED_UPLINK || current_cell.type == SLOT_TYPE_UPLINK) { + radio_event.radio_action = TSCH_RADIO_ACTION_TX; + } + +#define IS_DOTBOT +#ifdef IS_DOTBOT + if (current_cell.type == SLOT_TYPE_UPLINK && current_cell.assigned_node_id == NULL) { + // listen for beacons using the same frequency during a whole slotframe + radio_event.radio_action = TSCH_RADIO_ACTION_RX; + size_t beacon_channel = TSCH_N_BLE_REGULAR_FREQUENCIES + (_schedule_vars.slotframe_counter % TSCH_N_BLE_ADVERTISING_FREQUENCIES); + radio_event.frequency = _ble_chan_to_freq[beacon_channel]; + } +#endif + + return radio_event; +} + +uint8_t db_scheduler_get_frequency(slot_type_t slot_type, uint64_t asn, uint8_t channel_offset) { + if (slot_type == SLOT_TYPE_BEACON) { + // special handling in case the cell is a beacon + size_t beacon_channel = TSCH_N_BLE_REGULAR_FREQUENCIES + (_schedule_vars.current_beacon_cell_index++ % TSCH_N_BLE_ADVERTISING_FREQUENCIES); + uint8_t freq = _ble_chan_to_freq[beacon_channel]; + return freq; + } else { + // As per RFC 7554: + // frequency = F {(ASN + channelOffset) mod nFreq} + size_t freq_index = (asn + channel_offset) % TSCH_N_BLE_REGULAR_FREQUENCIES; + return _ble_chan_to_freq[freq_index]; + } +} + +//=========================== private ========================================== diff --git a/drv/tsch.h b/drv/tsch.h new file mode 100644 index 000000000..fa366e798 --- /dev/null +++ b/drv/tsch.h @@ -0,0 +1,58 @@ +#ifndef __TSCH_H +#define __TSCH_H + +/** + * @defgroup drv_tsch TSCH radio driver + * @ingroup drv + * @brief Driver for Time-Slotted Channel Hopping (TSCH) + * + * @{ + * @file + * @author Geovane Fedrecheski + * @copyright Inria, 2024-now + * @} + */ + +#include +#include +#include + +#include "gpio.h" +#include "radio.h" +#include "protocol.h" + +//=========================== defines ========================================== + +#define TSCH_TIMER_DEV 2 ///< HF timer device used for the TSCH scheduler +#define TSCH_TIMER_SLOT_CHANNEL 0 +#define TSCH_TIMER_INTRA_SLOT_CHANNEL 1 + +//=========================== variables ======================================== + +typedef void (*tsch_cb_t)(uint8_t *packet, uint8_t length); ///< Function pointer to the callback function called on packet receive + +typedef enum { + TSCH_RADIO_ACTION_TX, + TSCH_RADIO_ACTION_RX, + TSCH_RADIO_ACTION_SLEEP, +} tsch_radio_action_t; + +typedef struct { + tsch_radio_action_t radio_action; + uint8_t frequency; + uint16_t duration_us; +} tsch_radio_event_t; + +//=========================== prototypes ========================================== + +/** + * @brief Initializes the TSCH scheme + * + * @param[in] callback pointer to a function that will be called each time a packet is received. + * @param[in] radio_mode BLE mode used by the radio (1MBit, 2MBit, LR125KBit, LR500Kbit) + * @param[in] default_radio_app Which application to use for registration and sync messages + * + */ +void db_tsch_init(tsch_cb_t callback, db_radio_mode_t radio_mode, application_type_t default_radio_app); + +#endif diff --git a/drv/tsch/tsch.c b/drv/tsch/tsch.c new file mode 100644 index 000000000..e98865915 --- /dev/null +++ b/drv/tsch/tsch.c @@ -0,0 +1,56 @@ +/** + * @file + * @ingroup drv_tsch + * + * @brief Driver for Time-Slotted Channel Hopping (TSCH) + * + * @author Geovane Fedrecheski + * + * @copyright Inria, 2024 + */ +#include +#include +#include +#include +#include + +#include "tsch.h" +#include "scheduler.h" +#include "radio.h" +#include "timer_hf.h" +#include "protocol.h" +#include "device.h" +#if defined(NRF5340_XXAA) && defined(NRF_NETWORK) +#include "ipc.h" +#endif +//=========================== defines ========================================== + +typedef struct { + tsch_cb_t callback; ///< Function pointer, stores the callback to use in the RADIO_Irq handler. + application_type_t default_radio_app; ///< Which application to use for registration and sync messages +} tsch_vars_t; + +//=========================== variables ======================================== + +//========================== prototypes ======================================== + +//=========================== public =========================================== + +void db_tsch_tick(void) { + tsch_radio_event_t event = db_scheduler_tick(); + switch (event.radio_action) { + // case TSCH_RADIO_ACTION_TX: + // db_radio_set_tx(event.frequency); + // break; + // case TSCH_RADIO_ACTION_RX: + // db_radio_set_rx(event.frequency); + // break; + // case TSCH_RADIO_ACTION_SLEEP: + // db_radio_set_sleep(); + // break; + default: + break; + } +} + +//=========================== private ========================================== diff --git a/projects/01drv_scheduler/01drv_scheduler.c b/projects/01drv_scheduler/01drv_scheduler.c new file mode 100644 index 000000000..df6141434 --- /dev/null +++ b/projects/01drv_scheduler/01drv_scheduler.c @@ -0,0 +1,60 @@ +/** + * @file + * @ingroup drv_scheduler + * + * @brief Example on how to use the TSCH scheduler + * + * @author Geovane Fedrecheski + * + * @copyright Inria, 2024 + */ +#include +#include +#include + +#include "scheduler.h" +#include "tsch.h" +#include "radio.h" +#include "timer_hf.h" +#include "protocol.h" + +#define IS_DOTBOT + +/* Very simple test schedule */ +schedule_t schedule_test = { + .id = 10, // make sure it doesn't collide + .max_nodes = 0, + .backoff_n_min = 5, + .backoff_n_max = 9, + .slot_duration_us = 2024, + .n_cells = 3, + .cells = { + // Only downlink slots + {'D', 0, NULL}, + {'D', 1, NULL}, + {'D', 2, NULL}, + } +}; + +int main(void) { + puts("Scheduler test application"); + + // initialize high frequency timer + db_timer_hf_init(TSCH_TIMER_DEV); + + db_scheduler_init(&schedule_test); + // db_scheduler_set_schedule(5); + + uint8_t freq = db_scheduler_get_frequency(SLOT_TYPE_SHARED_UPLINK, 0, 0); + printf("Frequency: %d\n", freq); + + while (1) { + tsch_radio_event_t event = db_scheduler_tick(); + printf("Event: %d, %d, %d\n", event.radio_action, event.frequency, event.duration_us); + + // sleep for the duration of the slot + db_timer_hf_delay_us(TSCH_TIMER_DEV, event.duration_us); + + __WFE(); + } +} diff --git a/projects/01drv_scheduler/README.md b/projects/01drv_scheduler/README.md new file mode 100644 index 000000000..27e4354ec --- /dev/null +++ b/projects/01drv_scheduler/README.md @@ -0,0 +1 @@ +# TSCH Scheduler diff --git a/projects/01drv_tsch/01drv_tsch.c b/projects/01drv_tsch/01drv_tsch.c new file mode 100644 index 000000000..83af65577 --- /dev/null +++ b/projects/01drv_tsch/01drv_tsch.c @@ -0,0 +1,29 @@ +/** + * @file + * @ingroup drv_tsch + * + * @brief Example on how to use the TSCH driver + * + * @author Geovane Fedrecheski + * + * @copyright Inria, 2024 + */ +#include +#include +#include + +#include "tsch.h" +#include "scheduler.h" +#include "radio.h" +#include "timer_hf.h" +#include "protocol.h" + + + +int main(void) { + puts("TSCH test application"); + + while (1) { + __WFE(); + } +} diff --git a/projects/01drv_tsch/README.md b/projects/01drv_tsch/README.md new file mode 100644 index 000000000..ddd5add33 --- /dev/null +++ b/projects/01drv_tsch/README.md @@ -0,0 +1 @@ +# TSCH diff --git a/projects/projects-bsp-drv.emProject b/projects/projects-bsp-drv.emProject index 683b5a648..920482427 100644 --- a/projects/projects-bsp-drv.emProject +++ b/projects/projects-bsp-drv.emProject @@ -833,4 +833,54 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 73177d243b1521d7e203ce500f051b70b2464da1 Mon Sep 17 00:00:00 2001 From: Geovane Fedrecheski Date: Mon, 6 Jan 2025 10:32:23 +0100 Subject: [PATCH 02/17] tsch: fix dumb bugs, allow 32-bit duration_us --- drv/scheduler.h | 2 +- drv/scheduler/all_schedules.c | 10 ++++- drv/scheduler/scheduler.c | 47 +++++++++++++--------- drv/tsch.h | 8 ++-- projects/01drv_scheduler/01drv_scheduler.c | 19 ++++++--- 5 files changed, 55 insertions(+), 31 deletions(-) diff --git a/drv/scheduler.h b/drv/scheduler.h index 5e0498921..46cbf41d6 100644 --- a/drv/scheduler.h +++ b/drv/scheduler.h @@ -53,7 +53,7 @@ typedef struct { uint8_t max_nodes; // maximum number of nodes that can be scheduled, equivalent to the number of uplink slots uint8_t backoff_n_min; // minimum exponent for the backoff algorithm uint8_t backoff_n_max; // maximum exponent for the backoff algorithm - uint16_t slot_duration_us; // duration of a slot in microseconds + uint32_t slot_duration_us; // duration of a slot in microseconds size_t n_cells; // number of cells in this schedule cell_t cells[TSCH_N_CELLS_MAX]; // cells in this schedule. NOTE(FIXME?): the first 3 cells must be beacons } schedule_t; diff --git a/drv/scheduler/all_schedules.c b/drv/scheduler/all_schedules.c index e128932a0..f5bb69b9a 100644 --- a/drv/scheduler/all_schedules.c +++ b/drv/scheduler/all_schedules.c @@ -12,13 +12,19 @@ #define N_SCHEDULES 1 + 2 // account for the schedule that can be passed by the application during initialization +#ifndef TSCH_DEFAULT_SLOT_DURATION_US +// #define TSCH_DEFAULT_SLOT_DURATION_US 2024 +#define TSCH_DEFAULT_SLOT_DURATION_US 1000 * 1000 +#endif + /* Schedule with 11 slots, supporting up to 5 nodes */ schedule_t schedule_minuscule = { .id = 32, .max_nodes = 5, .backoff_n_min = 5, .backoff_n_max = 9, - .slot_duration_us = 2024, + // .slot_duration_us = 2024, + .slot_duration_us = TSCH_DEFAULT_SLOT_DURATION_US, .n_cells = 11, .cells = { // Begin with beacon cells. They use their own channel offsets and frequencies. @@ -43,7 +49,7 @@ schedule_t schedule_tiny = { .max_nodes = 11, .backoff_n_min = 5, .backoff_n_max = 9, - .slot_duration_us = 2024, + .slot_duration_us = TSCH_DEFAULT_SLOT_DURATION_US, .n_cells = 17, .cells = { // Begin with beacon cells. They use their own channel offsets and frequencies. diff --git a/drv/scheduler/scheduler.c b/drv/scheduler/scheduler.c index 88d0423f3..231dd6759 100644 --- a/drv/scheduler/scheduler.c +++ b/drv/scheduler/scheduler.c @@ -47,7 +47,7 @@ typedef struct { // counters and indexes uint64_t asn; // absolute slot number - uint8_t active_schedule_id; // id of the active schedule in available_schedules + schedule_t *active_schedule_ptr; // pointer to the currently active schedule uint32_t slotframe_counter; // used to cycle beacon frequencies through slotframes (when listening for beacons at uplink slots) size_t current_beacon_cell_index; @@ -68,14 +68,14 @@ void db_scheduler_init(schedule_t *application_schedule) { _schedule_vars.available_schedules[_schedule_vars.available_schedules_len++] = schedule_tiny; if (application_schedule != NULL) { _schedule_vars.available_schedules[_schedule_vars.available_schedules_len++] = *application_schedule; - _schedule_vars.active_schedule_id = application_schedule->id; + _schedule_vars.active_schedule_ptr = application_schedule; } } bool db_scheduler_set_schedule(uint8_t schedule_id) { for (size_t i = 0; i < N_SCHEDULES; i++) { if (_schedule_vars.available_schedules[i].id == schedule_id) { - _schedule_vars.active_schedule_id = i; + _schedule_vars.active_schedule_ptr = &_schedule_vars.available_schedules[i]; return true; } } @@ -83,20 +83,12 @@ bool db_scheduler_set_schedule(uint8_t schedule_id) { } tsch_radio_event_t db_scheduler_tick(void) { - schedule_t active_schedule = _schedule_vars.available_schedules[_schedule_vars.active_schedule_id]; + schedule_t active_schedule = *_schedule_vars.active_schedule_ptr; - // increment ASN so that nodes are in sync - _schedule_vars.asn++; - - // advance the current cell + // get the current cell size_t cell_index = _schedule_vars.asn % active_schedule.n_cells; cell_t current_cell = active_schedule.cells[cell_index]; - // if wrapped, keep track of how many slotframes have passed (used to cycle beacon frequencies) - if (cell_index == 0) { - _schedule_vars.slotframe_counter++; - } - tsch_radio_event_t radio_event = { .radio_action = TSCH_RADIO_ACTION_SLEEP, .duration_us = active_schedule.slot_duration_us, @@ -105,13 +97,24 @@ tsch_radio_event_t db_scheduler_tick(void) { radio_event.frequency = db_scheduler_get_frequency(current_cell.type, _schedule_vars.asn, current_cell.channel_offset); - if (current_cell.type == SLOT_TYPE_BEACON || current_cell.type == SLOT_TYPE_DOWNLINK) { - radio_event.radio_action = TSCH_RADIO_ACTION_RX; - } else if (current_cell.type == SLOT_TYPE_SHARED_UPLINK || current_cell.type == SLOT_TYPE_UPLINK) { - radio_event.radio_action = TSCH_RADIO_ACTION_TX; + switch (current_cell.type) { + case SLOT_TYPE_BEACON: + case SLOT_TYPE_DOWNLINK: + radio_event.radio_action = TSCH_RADIO_ACTION_RX; + break; + case SLOT_TYPE_SHARED_UPLINK: + radio_event.radio_action = TSCH_RADIO_ACTION_TX; + break; + case SLOT_TYPE_UPLINK: + if (current_cell.assigned_node_id != NULL) { + radio_event.radio_action = TSCH_RADIO_ACTION_TX; + } + break; + default: + break; } -#define IS_DOTBOT +// #define IS_DOTBOT #ifdef IS_DOTBOT if (current_cell.type == SLOT_TYPE_UPLINK && current_cell.assigned_node_id == NULL) { // listen for beacons using the same frequency during a whole slotframe @@ -121,6 +124,14 @@ tsch_radio_event_t db_scheduler_tick(void) { } #endif + // if the slotframe wrapped, keep track of how many slotframes have passed (used to cycle beacon frequencies) + if (cell_index == 0) { + _schedule_vars.slotframe_counter++; + } + + // increment ASN so that (1) nodes are in sync and (2) next time we get the next cell + _schedule_vars.asn++; + return radio_event; } diff --git a/drv/tsch.h b/drv/tsch.h index fa366e798..5337cffe8 100644 --- a/drv/tsch.h +++ b/drv/tsch.h @@ -32,15 +32,15 @@ typedef void (*tsch_cb_t)(uint8_t *packet, uint8_t length); ///< Function pointer to the callback function called on packet receive typedef enum { - TSCH_RADIO_ACTION_TX, - TSCH_RADIO_ACTION_RX, - TSCH_RADIO_ACTION_SLEEP, + TSCH_RADIO_ACTION_SLEEP = 'S', + TSCH_RADIO_ACTION_RX = 'R', + TSCH_RADIO_ACTION_TX = 'T', } tsch_radio_action_t; typedef struct { tsch_radio_action_t radio_action; uint8_t frequency; - uint16_t duration_us; + uint32_t duration_us; } tsch_radio_event_t; //=========================== prototypes ========================================== diff --git a/projects/01drv_scheduler/01drv_scheduler.c b/projects/01drv_scheduler/01drv_scheduler.c index df6141434..e0481fa8e 100644 --- a/projects/01drv_scheduler/01drv_scheduler.c +++ b/projects/01drv_scheduler/01drv_scheduler.c @@ -19,6 +19,7 @@ #include "protocol.h" #define IS_DOTBOT +#define TSCH_DEFAULT_SLOT_DURATION_US 1000 * 1000 /* Very simple test schedule */ schedule_t schedule_test = { @@ -26,13 +27,15 @@ schedule_t schedule_test = { .max_nodes = 0, .backoff_n_min = 5, .backoff_n_max = 9, - .slot_duration_us = 2024, + .slot_duration_us = TSCH_DEFAULT_SLOT_DURATION_US, .n_cells = 3, .cells = { // Only downlink slots - {'D', 0, NULL}, - {'D', 1, NULL}, + {'B', 0, NULL}, + {'S', 1, NULL}, {'D', 2, NULL}, + {'U', 3, NULL}, + {'U', 4, 1}, } }; @@ -43,18 +46,22 @@ int main(void) { db_timer_hf_init(TSCH_TIMER_DEV); db_scheduler_init(&schedule_test); - // db_scheduler_set_schedule(5); + db_scheduler_set_schedule(32); uint8_t freq = db_scheduler_get_frequency(SLOT_TYPE_SHARED_UPLINK, 0, 0); printf("Frequency: %d\n", freq); - while (1) { + for (int i = 0; i < 11; i++) { tsch_radio_event_t event = db_scheduler_tick(); - printf("Event: %d, %d, %d\n", event.radio_action, event.frequency, event.duration_us); + printf("Event: %c, %d, %d\n", event.radio_action, event.frequency, event.duration_us); // sleep for the duration of the slot db_timer_hf_delay_us(TSCH_TIMER_DEV, event.duration_us); + //__WFE(); + } + + while (1) { __WFE(); } } From 2f934f71eefec488b611618c8c8cc72ce5eb39cf Mon Sep 17 00:00:00 2001 From: Geovane Fedrecheski Date: Mon, 6 Jan 2025 15:07:09 +0100 Subject: [PATCH 03/17] tsch: schedule ticks ok for gateway and dotbot --- drv/scheduler.h | 9 +- drv/scheduler/all_schedules.c | 6 +- drv/scheduler/scheduler.c | 99 ++++++++++++++-------- drv/tsch.h | 1 + projects/01drv_scheduler/01drv_scheduler.c | 40 +++++---- 5 files changed, 97 insertions(+), 58 deletions(-) diff --git a/drv/scheduler.h b/drv/scheduler.h index 46cbf41d6..ca27ed7ad 100644 --- a/drv/scheduler.h +++ b/drv/scheduler.h @@ -35,12 +35,17 @@ //=========================== variables ======================================== +typedef enum { + NODE_TYPE_GATEWAY = 'G', + NODE_TYPE_DOTBOT = 'D', +} node_type_t; + typedef enum { SLOT_TYPE_BEACON = 'B', SLOT_TYPE_SHARED_UPLINK = 'S', SLOT_TYPE_DOWNLINK = 'D', SLOT_TYPE_UPLINK = 'U', -} slot_type_t; +} slot_type_t; // FIXME: slot_type or cell_type? typedef struct { slot_type_t type; @@ -67,7 +72,7 @@ typedef struct { * * @param[in] schedule Schedule to be used. */ -void db_scheduler_init(schedule_t *schedule); +void db_scheduler_init(node_type_t node_type, schedule_t *application_schedule); /** * @brief Advances the schedule by one cell/slot. diff --git a/drv/scheduler/all_schedules.c b/drv/scheduler/all_schedules.c index f5bb69b9a..41d336388 100644 --- a/drv/scheduler/all_schedules.c +++ b/drv/scheduler/all_schedules.c @@ -10,11 +10,11 @@ */ #include "scheduler.h" -#define N_SCHEDULES 1 + 2 // account for the schedule that can be passed by the application during initialization +#define TSCH_N_SCHEDULES 1 + 2 // account for the schedule that can be passed by the application during initialization #ifndef TSCH_DEFAULT_SLOT_DURATION_US -// #define TSCH_DEFAULT_SLOT_DURATION_US 2024 -#define TSCH_DEFAULT_SLOT_DURATION_US 1000 * 1000 +#define TSCH_DEFAULT_SLOT_DURATION_US 2024 +// #define TSCH_DEFAULT_SLOT_DURATION_US 1000 * 1000 #endif /* Schedule with 11 slots, supporting up to 5 nodes */ diff --git a/drv/scheduler/scheduler.c b/drv/scheduler/scheduler.c index 231dd6759..df8eca853 100644 --- a/drv/scheduler/scheduler.c +++ b/drv/scheduler/scheduler.c @@ -44,15 +44,15 @@ static const uint8_t _ble_chan_to_freq[40] = { }; typedef struct { + node_type_t node_type; // whether the node is a gateway or a dotbot + // counters and indexes uint64_t asn; // absolute slot number - schedule_t *active_schedule_ptr; // pointer to the currently active schedule uint32_t slotframe_counter; // used to cycle beacon frequencies through slotframes (when listening for beacons at uplink slots) - size_t current_beacon_cell_index; // static data - schedule_t available_schedules[N_SCHEDULES]; + schedule_t available_schedules[TSCH_N_SCHEDULES]; size_t available_schedules_len; } schedule_vars_t; @@ -60,10 +60,19 @@ static schedule_vars_t _schedule_vars = { 0 }; //========================== prototypes ======================================== +// Compute the radio action when the node is a gateway +void _compute_gateway_action(cell_t cell, tsch_radio_event_t *radio_event); + +// Compute the radio action when the node is a dotbot +void _compute_dotbot_action(cell_t cell, tsch_radio_event_t *radio_event); //=========================== public =========================================== -void db_scheduler_init(schedule_t *application_schedule) { +void db_scheduler_init(node_type_t node_type, schedule_t *application_schedule) { + _schedule_vars.node_type = node_type; + + if (_schedule_vars.available_schedules_len == TSCH_N_SCHEDULES) return; // FIXME: this is just to simplify debugging (allows calling init multiple times) + _schedule_vars.available_schedules[_schedule_vars.available_schedules_len++] = schedule_minuscule; _schedule_vars.available_schedules[_schedule_vars.available_schedules_len++] = schedule_tiny; if (application_schedule != NULL) { @@ -73,7 +82,7 @@ void db_scheduler_init(schedule_t *application_schedule) { } bool db_scheduler_set_schedule(uint8_t schedule_id) { - for (size_t i = 0; i < N_SCHEDULES; i++) { + for (size_t i = 0; i < TSCH_N_SCHEDULES; i++) { if (_schedule_vars.available_schedules[i].id == schedule_id) { _schedule_vars.active_schedule_ptr = &_schedule_vars.available_schedules[i]; return true; @@ -87,45 +96,22 @@ tsch_radio_event_t db_scheduler_tick(void) { // get the current cell size_t cell_index = _schedule_vars.asn % active_schedule.n_cells; - cell_t current_cell = active_schedule.cells[cell_index]; + cell_t cell = active_schedule.cells[cell_index]; tsch_radio_event_t radio_event = { .radio_action = TSCH_RADIO_ACTION_SLEEP, .duration_us = active_schedule.slot_duration_us, - .frequency = 0 + .frequency = db_scheduler_get_frequency(cell.type, _schedule_vars.asn, cell.channel_offset), + .slot_type = cell.type, // FIXME: only for debugging, remove before merge }; - - radio_event.frequency = db_scheduler_get_frequency(current_cell.type, _schedule_vars.asn, current_cell.channel_offset); - - switch (current_cell.type) { - case SLOT_TYPE_BEACON: - case SLOT_TYPE_DOWNLINK: - radio_event.radio_action = TSCH_RADIO_ACTION_RX; - break; - case SLOT_TYPE_SHARED_UPLINK: - radio_event.radio_action = TSCH_RADIO_ACTION_TX; - break; - case SLOT_TYPE_UPLINK: - if (current_cell.assigned_node_id != NULL) { - radio_event.radio_action = TSCH_RADIO_ACTION_TX; - } - break; - default: - break; + if (_schedule_vars.node_type == NODE_TYPE_GATEWAY) { + _compute_gateway_action(cell, &radio_event); + } else { + _compute_dotbot_action(cell, &radio_event); } -// #define IS_DOTBOT -#ifdef IS_DOTBOT - if (current_cell.type == SLOT_TYPE_UPLINK && current_cell.assigned_node_id == NULL) { - // listen for beacons using the same frequency during a whole slotframe - radio_event.radio_action = TSCH_RADIO_ACTION_RX; - size_t beacon_channel = TSCH_N_BLE_REGULAR_FREQUENCIES + (_schedule_vars.slotframe_counter % TSCH_N_BLE_ADVERTISING_FREQUENCIES); - radio_event.frequency = _ble_chan_to_freq[beacon_channel]; - } -#endif - // if the slotframe wrapped, keep track of how many slotframes have passed (used to cycle beacon frequencies) - if (cell_index == 0) { + if (_schedule_vars.asn != 0 && cell_index == 0) { _schedule_vars.slotframe_counter++; } @@ -138,7 +124,7 @@ tsch_radio_event_t db_scheduler_tick(void) { uint8_t db_scheduler_get_frequency(slot_type_t slot_type, uint64_t asn, uint8_t channel_offset) { if (slot_type == SLOT_TYPE_BEACON) { // special handling in case the cell is a beacon - size_t beacon_channel = TSCH_N_BLE_REGULAR_FREQUENCIES + (_schedule_vars.current_beacon_cell_index++ % TSCH_N_BLE_ADVERTISING_FREQUENCIES); + size_t beacon_channel = TSCH_N_BLE_REGULAR_FREQUENCIES + (asn % TSCH_N_BLE_ADVERTISING_FREQUENCIES); uint8_t freq = _ble_chan_to_freq[beacon_channel]; return freq; } else { @@ -150,3 +136,42 @@ uint8_t db_scheduler_get_frequency(slot_type_t slot_type, uint64_t asn, uint8_t } //=========================== private ========================================== + +void _compute_gateway_action(cell_t cell, tsch_radio_event_t *radio_event) { + switch (cell.type) { + case SLOT_TYPE_BEACON: + case SLOT_TYPE_DOWNLINK: + radio_event->radio_action = TSCH_RADIO_ACTION_TX; + break; + case SLOT_TYPE_SHARED_UPLINK: + case SLOT_TYPE_UPLINK: + radio_event->radio_action = TSCH_RADIO_ACTION_RX; + break; + } +} + +void _compute_dotbot_action(cell_t cell, tsch_radio_event_t *radio_event) { + switch (cell.type) { + case SLOT_TYPE_BEACON: + case SLOT_TYPE_DOWNLINK: + radio_event->radio_action = TSCH_RADIO_ACTION_RX; + break; + case SLOT_TYPE_SHARED_UPLINK: + // TODO: implement backoff algorithm + radio_event->radio_action = TSCH_RADIO_ACTION_TX; + break; + case SLOT_TYPE_UPLINK: + if (cell.assigned_node_id != NULL) { // TODO: check if the assigned node is *this* node + radio_event->radio_action = TSCH_RADIO_ACTION_TX; + } else { + // OPTIMIZATION: listen for beacons during unassigned uplink slot + // listen to the same beacon frequency for a whole slotframe + radio_event->radio_action = TSCH_RADIO_ACTION_RX; + size_t beacon_channel = TSCH_N_BLE_REGULAR_FREQUENCIES + (_schedule_vars.slotframe_counter % TSCH_N_BLE_ADVERTISING_FREQUENCIES); + radio_event->frequency = _ble_chan_to_freq[beacon_channel]; + } + break; + default: + break; + } +} diff --git a/drv/tsch.h b/drv/tsch.h index 5337cffe8..3c8e17a1b 100644 --- a/drv/tsch.h +++ b/drv/tsch.h @@ -41,6 +41,7 @@ typedef struct { tsch_radio_action_t radio_action; uint8_t frequency; uint32_t duration_us; + char slot_type; // FIXME: only for debugging, remove before merge } tsch_radio_event_t; //=========================== prototypes ========================================== diff --git a/projects/01drv_scheduler/01drv_scheduler.c b/projects/01drv_scheduler/01drv_scheduler.c index e0481fa8e..c1a86ce8c 100644 --- a/projects/01drv_scheduler/01drv_scheduler.c +++ b/projects/01drv_scheduler/01drv_scheduler.c @@ -17,48 +17,56 @@ #include "radio.h" #include "timer_hf.h" #include "protocol.h" +#include "device.h" #define IS_DOTBOT -#define TSCH_DEFAULT_SLOT_DURATION_US 1000 * 1000 +//#define TSCH_DEFAULT_SLOT_DURATION_US 1000 * 1000 +#define TSCH_DEFAULT_SLOT_DURATION_US 2024 /* Very simple test schedule */ schedule_t schedule_test = { .id = 10, // make sure it doesn't collide - .max_nodes = 0, + .max_nodes = 2, .backoff_n_min = 5, .backoff_n_max = 9, .slot_duration_us = TSCH_DEFAULT_SLOT_DURATION_US, - .n_cells = 3, + .n_cells = 5, .cells = { // Only downlink slots {'B', 0, NULL}, {'S', 1, NULL}, {'D', 2, NULL}, {'U', 3, NULL}, - {'U', 4, 1}, + {'U', 4, NULL}, } }; +extern schedule_t schedule_minuscule; + int main(void) { - puts("Scheduler test application"); + //schedule_t schedule = schedule_test; + //schedule.cells[4].assigned_node_id = db_device_id(); // assign itself to the last uplink slot + + schedule_t schedule = schedule_minuscule; // initialize high frequency timer db_timer_hf_init(TSCH_TIMER_DEV); - db_scheduler_init(&schedule_test); - db_scheduler_set_schedule(32); - - uint8_t freq = db_scheduler_get_frequency(SLOT_TYPE_SHARED_UPLINK, 0, 0); - printf("Frequency: %d\n", freq); + db_scheduler_init(NODE_TYPE_GATEWAY, &schedule); + //db_scheduler_init(NODE_TYPE_DOTBOT, &schedule); - for (int i = 0; i < 11; i++) { - tsch_radio_event_t event = db_scheduler_tick(); - printf("Event: %c, %d, %d\n", event.radio_action, event.frequency, event.duration_us); + size_t n_runs = 2; + for (size_t j = 0; j < n_runs; j++) { + for (size_t i = 0; i < schedule.n_cells; i++) { + tsch_radio_event_t event = db_scheduler_tick(); + printf("Event %c: %c, %d, %d\n", event.slot_type, event.radio_action, event.frequency, event.duration_us); - // sleep for the duration of the slot - db_timer_hf_delay_us(TSCH_TIMER_DEV, event.duration_us); + // sleep for the duration of the slot + db_timer_hf_delay_us(TSCH_TIMER_DEV, event.duration_us); - //__WFE(); + //__WFE(); + } + puts("."); } while (1) { From 5dac45f18aa769776901c1f8ba3c06755a59e6c5 Mon Sep 17 00:00:00 2001 From: Geovane Fedrecheski Date: Mon, 6 Jan 2025 15:46:08 +0100 Subject: [PATCH 04/17] tsch: can assign and de-assign uplink cells --- drv/scheduler.h | 18 ++++++++++++++ drv/scheduler/scheduler.c | 24 ++++++++++++++++++- projects/01drv_scheduler/01drv_scheduler.c | 28 +++++++++++++--------- 3 files changed, 58 insertions(+), 12 deletions(-) diff --git a/drv/scheduler.h b/drv/scheduler.h index ca27ed7ad..736500179 100644 --- a/drv/scheduler.h +++ b/drv/scheduler.h @@ -92,6 +92,24 @@ tsch_radio_event_t db_scheduler_tick(void); */ bool db_scheduler_set_schedule(uint8_t schedule_id); +/** + * @brief Assigns the next available uplink cell to a given node. + * + * @param[in] node_id Node ID + * + * @return true if the uplink cell was successfully assigned, false otherwise (e.g., all uplink cells are already assigned) + */ +bool db_scheduler_assign_next_available_uplink_cell(uint64_t node_id); + +/** + * @brief Deassigns the uplink cell assigned to a given node. + * + * @param[in] node_id Node ID + * + * @return true if the uplink cell was successfully deassigned, false otherwise + */ +bool db_scheduler_deassign_uplink_cell(uint64_t node_id); + /** * @brief Computes the frequency to be used in a given slot. * diff --git a/drv/scheduler/scheduler.c b/drv/scheduler/scheduler.c index df8eca853..18a89c640 100644 --- a/drv/scheduler/scheduler.c +++ b/drv/scheduler/scheduler.c @@ -91,6 +91,28 @@ bool db_scheduler_set_schedule(uint8_t schedule_id) { return false; } +bool db_scheduler_assign_next_available_uplink_cell(uint64_t node_id) { + for (size_t i = 0; i < _schedule_vars.active_schedule_ptr->n_cells; i++) { + cell_t *cell = &_schedule_vars.active_schedule_ptr->cells[i]; + if (cell->type == SLOT_TYPE_UPLINK && cell->assigned_node_id == NULL) { + cell->assigned_node_id = node_id; + return true; + } + } + return false; +} + +bool db_scheduler_deassign_uplink_cell(uint64_t node_id) { + for (size_t i = 0; i < _schedule_vars.active_schedule_ptr->n_cells; i++) { + cell_t *cell = &_schedule_vars.active_schedule_ptr->cells[i]; + if (cell->type == SLOT_TYPE_UPLINK && cell->assigned_node_id == node_id) { + cell->assigned_node_id = NULL; + return true; + } + } + return false; +} + tsch_radio_event_t db_scheduler_tick(void) { schedule_t active_schedule = *_schedule_vars.active_schedule_ptr; @@ -161,7 +183,7 @@ void _compute_dotbot_action(cell_t cell, tsch_radio_event_t *radio_event) { radio_event->radio_action = TSCH_RADIO_ACTION_TX; break; case SLOT_TYPE_UPLINK: - if (cell.assigned_node_id != NULL) { // TODO: check if the assigned node is *this* node + if (cell.assigned_node_id == db_device_id()) { radio_event->radio_action = TSCH_RADIO_ACTION_TX; } else { // OPTIMIZATION: listen for beacons during unassigned uplink slot diff --git a/projects/01drv_scheduler/01drv_scheduler.c b/projects/01drv_scheduler/01drv_scheduler.c index c1a86ce8c..668ea294f 100644 --- a/projects/01drv_scheduler/01drv_scheduler.c +++ b/projects/01drv_scheduler/01drv_scheduler.c @@ -44,29 +44,35 @@ schedule_t schedule_test = { extern schedule_t schedule_minuscule; int main(void) { - //schedule_t schedule = schedule_test; - //schedule.cells[4].assigned_node_id = db_device_id(); // assign itself to the last uplink slot - - schedule_t schedule = schedule_minuscule; - // initialize high frequency timer db_timer_hf_init(TSCH_TIMER_DEV); - db_scheduler_init(NODE_TYPE_GATEWAY, &schedule); - //db_scheduler_init(NODE_TYPE_DOTBOT, &schedule); + // initialize schedule + schedule_t schedule = schedule_minuscule; + node_type_t node_type = NODE_TYPE_DOTBOT; + db_scheduler_init(node_type, &schedule); - size_t n_runs = 2; - for (size_t j = 0; j < n_runs; j++) { + printf("Device of type %c and id %llx is using schedule %d\n\n", node_type, db_device_id(), schedule.id); + + // loop n_slotframes*n_cells times and make the scheduler tick + // also, try to assign and deassign uplink cell at specific slotframes + size_t n_slotframes = 4; + for (size_t j = 0; j < n_slotframes; j++) { for (size_t i = 0; i < schedule.n_cells; i++) { tsch_radio_event_t event = db_scheduler_tick(); printf("Event %c: %c, %d, %d\n", event.slot_type, event.radio_action, event.frequency, event.duration_us); // sleep for the duration of the slot db_timer_hf_delay_us(TSCH_TIMER_DEV, event.duration_us); - - //__WFE(); } puts("."); + if (j == 0 && !db_scheduler_assign_next_available_uplink_cell(db_device_id())) { // try to assign at the end of first slotframe + printf("Failed to assign uplink cell\n"); + return 1; + } else if (j == n_slotframes-2 && !db_scheduler_deassign_uplink_cell(db_device_id())) { // try to deassign at the end of the second-to-last slotframe + printf("Failed to deassign uplink cell\n"); + return 1; + } } while (1) { From e5d3da2aba46d0dd4c8af8da6c0d59b6e0b9ccca Mon Sep 17 00:00:00 2001 From: Geovane Fedrecheski Date: Mon, 6 Jan 2025 17:01:55 +0100 Subject: [PATCH 05/17] tsch: refactor test schedules into own file --- projects/01drv_scheduler/01drv_scheduler.c | 24 +-------- projects/01drv_scheduler/test_schedules.c | 59 ++++++++++++++++++++++ 2 files changed, 61 insertions(+), 22 deletions(-) create mode 100644 projects/01drv_scheduler/test_schedules.c diff --git a/projects/01drv_scheduler/01drv_scheduler.c b/projects/01drv_scheduler/01drv_scheduler.c index 668ea294f..6716d9d57 100644 --- a/projects/01drv_scheduler/01drv_scheduler.c +++ b/projects/01drv_scheduler/01drv_scheduler.c @@ -19,28 +19,8 @@ #include "protocol.h" #include "device.h" -#define IS_DOTBOT -//#define TSCH_DEFAULT_SLOT_DURATION_US 1000 * 1000 -#define TSCH_DEFAULT_SLOT_DURATION_US 2024 - -/* Very simple test schedule */ -schedule_t schedule_test = { - .id = 10, // make sure it doesn't collide - .max_nodes = 2, - .backoff_n_min = 5, - .backoff_n_max = 9, - .slot_duration_us = TSCH_DEFAULT_SLOT_DURATION_US, - .n_cells = 5, - .cells = { - // Only downlink slots - {'B', 0, NULL}, - {'S', 1, NULL}, - {'D', 2, NULL}, - {'U', 3, NULL}, - {'U', 4, NULL}, - } -}; - +// make some schedules available for testing +#include "test_schedules.c" extern schedule_t schedule_minuscule; int main(void) { diff --git a/projects/01drv_scheduler/test_schedules.c b/projects/01drv_scheduler/test_schedules.c new file mode 100644 index 000000000..86c0b1937 --- /dev/null +++ b/projects/01drv_scheduler/test_schedules.c @@ -0,0 +1,59 @@ +#include "scheduler.h" + +#define IS_DOTBOT +#define TSCH_DEFAULT_SLOT_DURATION_US 2024 +//#define TSCH_DEFAULT_SLOT_DURATION_US 1000 * 1000 + +/* Very simple test schedule */ +schedule_t schedule_test = { + .id = 10, // make sure it doesn't collide + .max_nodes = 2, + .backoff_n_min = 5, + .backoff_n_max = 9, + .slot_duration_us = TSCH_DEFAULT_SLOT_DURATION_US, + .n_cells = 5, + .cells = { + // Only downlink slots + {'B', 0, NULL}, + {'S', 1, NULL}, + {'D', 2, NULL}, + {'U', 3, NULL}, + {'U', 4, NULL}, + } +}; + +/* Uplink only test schedule */ +schedule_t schedule_all_uplink = { + .id = 10, // make sure it doesn't collide + .max_nodes = 2, + .backoff_n_min = 5, + .backoff_n_max = 9, + .slot_duration_us = TSCH_DEFAULT_SLOT_DURATION_US, + .n_cells = 5, + .cells = { + // Only downlink slots + {'U', 0, NULL}, + {'U', 1, NULL}, + {'U', 2, NULL}, + {'U', 3, NULL}, + {'U', 4, NULL}, + } +}; + +/* Downlink only test schedule */ +schedule_t schedule_all_downlink = { + .id = 10, // make sure it doesn't collide + .max_nodes = 2, + .backoff_n_min = 5, + .backoff_n_max = 9, + .slot_duration_us = TSCH_DEFAULT_SLOT_DURATION_US, + .n_cells = 5, + .cells = { + // Only downlink slots + {'D', 0, NULL}, + {'D', 1, NULL}, + {'D', 2, NULL}, + {'D', 3, NULL}, + {'D', 4, NULL}, + } +}; From 8bb52fc8361f24842160078a9a69ed4fa18f8835 Mon Sep 17 00:00:00 2001 From: Geovane Fedrecheski Date: Mon, 6 Jan 2025 20:04:40 +0100 Subject: [PATCH 06/17] tsch: implement basic tsch mac driver --- drv/scheduler/all_schedules.c | 54 ++++++++- drv/tsch.h | 16 +-- drv/tsch/tsch.c | 121 ++++++++++++++++--- projects/01bsp_radio_txrx/01bsp_radio_txrx.c | 23 ++-- projects/01drv_scheduler/test_schedules.c | 4 +- projects/01drv_tsch/01drv_tsch.c | 53 +++++++- projects/projects-bsp-drv.emProject | 2 +- 7 files changed, 239 insertions(+), 34 deletions(-) diff --git a/drv/scheduler/all_schedules.c b/drv/scheduler/all_schedules.c index 41d336388..31043904b 100644 --- a/drv/scheduler/all_schedules.c +++ b/drv/scheduler/all_schedules.c @@ -19,7 +19,7 @@ /* Schedule with 11 slots, supporting up to 5 nodes */ schedule_t schedule_minuscule = { - .id = 32, + .id = 6, .max_nodes = 5, .backoff_n_min = 5, .backoff_n_max = 9, @@ -74,3 +74,55 @@ schedule_t schedule_tiny = { } }; +/* Schedule with 41 slots, supporting up to 29 nodes */ +schedule_t schedule_small = { + .id = 4, + .max_nodes = 29, + .backoff_n_min = 5, + .backoff_n_max = 9, + .slot_duration_us = TSCH_DEFAULT_SLOT_DURATION_US, + .n_cells = 41, + .cells = { + {'B', 0, NULL}, + {'B', 1, NULL}, + {'B', 2, NULL}, + {'S', 36, NULL}, + {'D', 20, NULL}, + {'U', 13, NULL}, + {'U', 27, NULL}, + {'U', 29, NULL}, + {'U', 9, NULL}, + {'D', 0, NULL}, + {'U', 4, NULL}, + {'U', 33, NULL}, + {'U', 3, NULL}, + {'U', 30, NULL}, + {'U', 31, NULL}, + {'S', 22, NULL}, + {'D', 15, NULL}, + {'U', 11, NULL}, + {'U', 16, NULL}, + {'U', 24, NULL}, + {'U', 21, NULL}, + {'D', 2, NULL}, + {'U', 19, NULL}, + {'U', 10, NULL}, + {'U', 25, NULL}, + {'U', 34, NULL}, + {'U', 14, NULL}, + {'S', 28, NULL}, + {'D', 32, NULL}, + {'U', 1, NULL}, + {'U', 5, NULL}, + {'U', 18, NULL}, + {'U', 7, NULL}, + {'D', 23, NULL}, + {'U', 12, NULL}, + {'U', 17, NULL}, + {'U', 6, NULL}, + {'U', 35, NULL}, + {'U', 8, NULL}, + {'U', 37, NULL}, + {'U', 26, NULL}, + } +}; diff --git a/drv/tsch.h b/drv/tsch.h index 3c8e17a1b..65b9feed4 100644 --- a/drv/tsch.h +++ b/drv/tsch.h @@ -17,10 +17,6 @@ #include #include -#include "gpio.h" -#include "radio.h" -#include "protocol.h" - //=========================== defines ========================================== #define TSCH_TIMER_DEV 2 ///< HF timer device used for the TSCH scheduler @@ -37,6 +33,14 @@ typedef enum { TSCH_RADIO_ACTION_TX = 'T', } tsch_radio_action_t; +typedef enum { + TSCH_PACKET_TYPE_BEACON = 1, + TSCH_PACKET_TYPE_JOIN_REQUEST = 2, + TSCH_PACKET_TYPE_JOIN_RESPONSE = 3, + TSCH_PACKET_TYPE_INFRASTRUCTURE_DATA = 8, + TSCH_PACKET_TYPE_EXPERIMENT_DATA = 9, +} tsch_packet_type_t; // TODO: move to protocol.h, but that will be part of a larger refactoring + typedef struct { tsch_radio_action_t radio_action; uint8_t frequency; @@ -50,10 +54,8 @@ typedef struct { * @brief Initializes the TSCH scheme * * @param[in] callback pointer to a function that will be called each time a packet is received. - * @param[in] radio_mode BLE mode used by the radio (1MBit, 2MBit, LR125KBit, LR500Kbit) - * @param[in] default_radio_app Which application to use for registration and sync messages * */ -void db_tsch_init(tsch_cb_t callback, db_radio_mode_t radio_mode, application_type_t default_radio_app); +void db_tsch_init(tsch_cb_t callback); #endif diff --git a/drv/tsch/tsch.c b/drv/tsch/tsch.c index e98865915..cba0b9282 100644 --- a/drv/tsch/tsch.c +++ b/drv/tsch/tsch.c @@ -25,32 +25,123 @@ #endif //=========================== defines ========================================== +//=========================== variables ======================================== + +typedef enum { + TSCH_STATE_WAIT_TO_RX, + TSCH_STATE_STARTED_RX, + TSCH_STATE_DONE_RX, + // etc. +} tsch_state_t; // TODO: actually use this state in the handlers below + typedef struct { - tsch_cb_t callback; ///< Function pointer, stores the callback to use in the RADIO_Irq handler. - application_type_t default_radio_app; ///< Which application to use for registration and sync messages + tsch_cb_t callback; ///< Function pointer, stores the callback to use in the RADIO_Irq handler. + uint64_t device_id; ///< Device ID + tsch_state_t state; ///< State of the TSCH state machine } tsch_vars_t; -//=========================== variables ======================================== +static tsch_vars_t _tsch_vars = { 0 }; + +uint8_t default_packet[] = { + 0x08, // size of the packet + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 +}; //========================== prototypes ======================================== +/** + * @brief Interrupt handler for the slot-wise ticking of the TSCH scheduler. + */ +static void _timer_tsch_slot_handler(void); + +/* +* @brief Callback function passed to the radio driver. +*/ +static void _tsch_callback(uint8_t *packet, uint8_t length); + //=========================== public =========================================== -void db_tsch_tick(void) { +void db_tsch_init(tsch_cb_t callback) { + // initialize the high frequency clock + db_timer_hf_init(TSCH_TIMER_DEV); + + // initialize the radio + db_radio_init(&_tsch_callback, DB_RADIO_BLE_2MBit); // set the radio callback to our tsch catch function + + // Save the user callback to use in our interruption + _tsch_vars.callback = callback; + + _tsch_vars.device_id = db_device_id(); + + // NOTE: assume the scheduler has already been initialized by the application + + // start the ticking immediately + db_timer_hf_set_oneshot_us(TSCH_TIMER_DEV, TSCH_TIMER_SLOT_CHANNEL, 100, _timer_tsch_slot_handler); +} + +//=========================== private ========================================== + + +// TODO: manipulate the _tsch_vars.state + + +void _timer_tsch_slot_handler(void) { + // FIXME: for some reason, it just receives (beacon) packets on frequency 26, and it is always "CRC error" + tsch_radio_event_t event = db_scheduler_tick(); + printf("Event %c: %c, %d, %d\n", event.slot_type, event.radio_action, event.frequency, event.duration_us); // FIXME: only for debugging, remove before merge + switch (event.radio_action) { - // case TSCH_RADIO_ACTION_TX: - // db_radio_set_tx(event.frequency); - // break; - // case TSCH_RADIO_ACTION_RX: - // db_radio_set_rx(event.frequency); - // break; - // case TSCH_RADIO_ACTION_SLEEP: - // db_radio_set_sleep(); - // break; - default: + case TSCH_RADIO_ACTION_TX: + db_radio_disable(); + db_radio_set_frequency(event.frequency); + // TODO: send a real packet: + // - from the application queue + // - a beacon, if it is a beacon slot and the node is a gateway + db_radio_tx(default_packet, sizeof(default_packet)); + break; + case TSCH_RADIO_ACTION_RX: + // TODO: if this is a DotBot, implement re-synchronization logic + db_radio_disable(); + db_radio_set_frequency(event.frequency); + db_radio_rx(); + break; + case TSCH_RADIO_ACTION_SLEEP: + db_radio_disable(); break; } + + // schedule the next tick + // FIXME: compensate for the time spent in the instructions above. For now, just using 'event.duration_us' will do. + db_timer_hf_set_oneshot_us(TSCH_TIMER_DEV, TSCH_TIMER_SLOT_CHANNEL, event.duration_us, _timer_tsch_slot_handler); } -//=========================== private ========================================== +static void _tsch_callback(uint8_t *packet, uint8_t length) { + uint8_t *ptk_ptr = packet; + protocol_header_t *header = (protocol_header_t *)ptk_ptr; + + // Check destination address matches + if (header->dst != DB_BROADCAST_ADDRESS && header->dst != _tsch_vars.device_id) { + return; + } + + // Check version is supported + if (header->version != DB_FIRMWARE_VERSION) { + return; + } + + switch (header->type) { + case TSCH_PACKET_TYPE_BEACON: + { + // ... + } break; + + // This is not a TSCH packet, send to the user callback + default: + { + if (_tsch_vars.callback) { + _tsch_vars.callback(packet, length); + } + } + } +} diff --git a/projects/01bsp_radio_txrx/01bsp_radio_txrx.c b/projects/01bsp_radio_txrx/01bsp_radio_txrx.c index cc2b71caf..813bd33d5 100644 --- a/projects/01bsp_radio_txrx/01bsp_radio_txrx.c +++ b/projects/01bsp_radio_txrx/01bsp_radio_txrx.c @@ -49,9 +49,17 @@ static const gpio_t _dbg_pin = { .port = DB_LED1_PORT, .pin = DB_LED1_PIN }; //=========================== functions ========================================= +//static void radio_callback(uint8_t *packet, uint8_t length) { +// db_gpio_toggle(&_dbg_pin); +// printf("(%dB): %s, RSSI: %i\n", length, (char *)packet, db_radio_rssi()); +//} + static void radio_callback(uint8_t *packet, uint8_t length) { - db_gpio_toggle(&_dbg_pin); - printf("(%dB): %s, RSSI: %i\n", length, (char *)packet, db_radio_rssi()); + printf("Received packet of length %d\n", length); + for (uint8_t i = 0; i < length; i++) { + printf("%02x ", packet[i]); + } + puts(""); } //=========================== main ============================================== @@ -71,13 +79,14 @@ int main(void) { //=========================== Configure Radio =============================== - db_radio_init(&radio_callback, DOTBOT_GW_RADIO_MODE); - db_radio_set_channel(CHANNEL); + db_radio_init(&radio_callback, DB_RADIO_BLE_2MBit); + db_radio_set_frequency(2); // 2 is one of the BLE advertising frequencies db_radio_rx(); + puts("Receiving on frequency 2..."); while (1) { - db_radio_disable(); - db_radio_tx((uint8_t *)packet_tx, sizeof(packet_tx) / sizeof(packet_tx[0])); - db_timer_hf_delay_ms(0, DELAY_MS); + //db_radio_disable(); + //db_radio_tx((uint8_t *)packet_tx, sizeof(packet_tx) / sizeof(packet_tx[0])); + db_timer_hf_delay_ms(0, 10); } } diff --git a/projects/01drv_scheduler/test_schedules.c b/projects/01drv_scheduler/test_schedules.c index 86c0b1937..43bcd6745 100644 --- a/projects/01drv_scheduler/test_schedules.c +++ b/projects/01drv_scheduler/test_schedules.c @@ -1,8 +1,8 @@ #include "scheduler.h" #define IS_DOTBOT -#define TSCH_DEFAULT_SLOT_DURATION_US 2024 -//#define TSCH_DEFAULT_SLOT_DURATION_US 1000 * 1000 +// #define TSCH_DEFAULT_SLOT_DURATION_US 2024 +#define TSCH_DEFAULT_SLOT_DURATION_US 1000 * 1000 /* Very simple test schedule */ schedule_t schedule_test = { diff --git a/projects/01drv_tsch/01drv_tsch.c b/projects/01drv_tsch/01drv_tsch.c index 83af65577..048bda3be 100644 --- a/projects/01drv_tsch/01drv_tsch.c +++ b/projects/01drv_tsch/01drv_tsch.c @@ -17,13 +17,64 @@ #include "radio.h" #include "timer_hf.h" #include "protocol.h" +#include "device.h" +//#define TSCH_DEFAULT_SLOT_DURATION_US 2024 +#define TSCH_DEFAULT_SLOT_DURATION_US 1000 * 20 +/* Very simple test schedule */ +schedule_t schedule_test = { + .id = 32, // make sure it doesn't collide + .max_nodes = 0, + .backoff_n_min = 5, + .backoff_n_max = 9, + .slot_duration_us = TSCH_DEFAULT_SLOT_DURATION_US, + .n_cells = 5, + .cells = { + //{'B', 0, NULL}, + //{'S', 1, NULL}, + //{'D', 2, NULL}, + //{'U', 3, NULL}, + //{'U', 4, NULL}, + + {'B', 0, NULL}, + {'B', 1, NULL}, + {'B', 2, NULL}, + {'B', 3, NULL}, + {'B', 4, NULL}, + + //{'U', 0, NULL}, + //{'U', 1, NULL}, + //{'U', 2, NULL}, + //{'U', 3, NULL}, + //{'U', 4, NULL}, + } +}; + +extern schedule_t schedule_minuscule, schedule_small; + +static void radio_callback(uint8_t *packet, uint8_t length); int main(void) { - puts("TSCH test application"); + // initialize schedule + schedule_t schedule = schedule_test; + schedule.slot_duration_us = TSCH_DEFAULT_SLOT_DURATION_US; + node_type_t node_type = NODE_TYPE_GATEWAY; + db_scheduler_init(node_type, &schedule); + printf("Device of type %c and id %llx is using schedule %d\n\n", node_type, db_device_id(), schedule.id); + + // initialize the TSCH driver + db_tsch_init(radio_callback); while (1) { __WFE(); } } + +static void radio_callback(uint8_t *packet, uint8_t length) { + printf("Received packet of length %d\n", length); + for (uint8_t i = 0; i < length; i++) { + printf("%02x ", packet[i]); + } + puts(""); +} diff --git a/projects/projects-bsp-drv.emProject b/projects/projects-bsp-drv.emProject index 920482427..6891f90ef 100644 --- a/projects/projects-bsp-drv.emProject +++ b/projects/projects-bsp-drv.emProject @@ -836,7 +836,7 @@ From fe059bece972e750657fbfbaf116dc1c07902195 Mon Sep 17 00:00:00 2001 From: Geovane Fedrecheski Date: Tue, 7 Jan 2025 08:39:57 +0100 Subject: [PATCH 07/17] tsch: frequency hopping works!! can rx from test app --- drv/tsch.h | 22 +++++++++++++++++- drv/tsch/tsch.c | 38 ++++++++++---------------------- projects/01drv_tsch/01drv_tsch.c | 6 ++--- 3 files changed, 36 insertions(+), 30 deletions(-) diff --git a/drv/tsch.h b/drv/tsch.h index 65b9feed4..667f7de19 100644 --- a/drv/tsch.h +++ b/drv/tsch.h @@ -33,13 +33,33 @@ typedef enum { TSCH_RADIO_ACTION_TX = 'T', } tsch_radio_action_t; +// ==== BEGIN TODO: move to protocol.h, but that will be part of a larger refactoring ==== typedef enum { TSCH_PACKET_TYPE_BEACON = 1, TSCH_PACKET_TYPE_JOIN_REQUEST = 2, TSCH_PACKET_TYPE_JOIN_RESPONSE = 3, TSCH_PACKET_TYPE_INFRASTRUCTURE_DATA = 8, TSCH_PACKET_TYPE_EXPERIMENT_DATA = 9, -} tsch_packet_type_t; // TODO: move to protocol.h, but that will be part of a larger refactoring +} tsch_packet_type_t; + +// general packet header +typedef struct { + uint8_t version; + tsch_packet_type_t type; + uint64_t dst; + uint64_t src; +} tsch_packet_header_t; + +// beacon packet +typedef struct { + uint8_t version; + tsch_packet_type_t type; + uint64_t src; + uint8_t remaining_capacity; + uint8_t active_schedule_id; + uint64_t asn; +} tsch_beacon_packet_header_t; +// ==== END TODO: move to protocol.h, but that will be part of a larger refactoring ====== typedef struct { tsch_radio_action_t radio_action; diff --git a/drv/tsch/tsch.c b/drv/tsch/tsch.c index cba0b9282..ae19bff78 100644 --- a/drv/tsch/tsch.c +++ b/drv/tsch/tsch.c @@ -35,7 +35,7 @@ typedef enum { } tsch_state_t; // TODO: actually use this state in the handlers below typedef struct { - tsch_cb_t callback; ///< Function pointer, stores the callback to use in the RADIO_Irq handler. + tsch_cb_t application_callback; ///< Function pointer, stores the application callback uint64_t device_id; ///< Device ID tsch_state_t state; ///< State of the TSCH state machine } tsch_vars_t; @@ -61,15 +61,15 @@ static void _tsch_callback(uint8_t *packet, uint8_t length); //=========================== public =========================================== -void db_tsch_init(tsch_cb_t callback) { +void db_tsch_init(tsch_cb_t application_callback) { // initialize the high frequency clock db_timer_hf_init(TSCH_TIMER_DEV); // initialize the radio db_radio_init(&_tsch_callback, DB_RADIO_BLE_2MBit); // set the radio callback to our tsch catch function - // Save the user callback to use in our interruption - _tsch_vars.callback = callback; + // Save the application callback to use in our interruption + _tsch_vars.application_callback = application_callback; _tsch_vars.device_id = db_device_id(); @@ -117,31 +117,17 @@ void _timer_tsch_slot_handler(void) { } static void _tsch_callback(uint8_t *packet, uint8_t length) { - uint8_t *ptk_ptr = packet; - protocol_header_t *header = (protocol_header_t *)ptk_ptr; + printf("Received packet of length %d\n", length); - // Check destination address matches - if (header->dst != DB_BROADCAST_ADDRESS && header->dst != _tsch_vars.device_id) { + // Check if the packet is big enough to have a header + if (length < sizeof(protocol_header_t)) { return; } - // Check version is supported - if (header->version != DB_FIRMWARE_VERSION) { - return; - } - - switch (header->type) { - case TSCH_PACKET_TYPE_BEACON: - { - // ... - } break; - - // This is not a TSCH packet, send to the user callback - default: - { - if (_tsch_vars.callback) { - _tsch_vars.callback(packet, length); - } - } + // Check if it is a beacon + if (packet[1] == TSCH_PACKET_TYPE_BEACON) { + // _tsch_handle_beacon(packet, length); + } else if (_tsch_vars.application_callback) { + _tsch_vars.application_callback(packet, length); } } diff --git a/projects/01drv_tsch/01drv_tsch.c b/projects/01drv_tsch/01drv_tsch.c index 048bda3be..6da4b9758 100644 --- a/projects/01drv_tsch/01drv_tsch.c +++ b/projects/01drv_tsch/01drv_tsch.c @@ -20,7 +20,7 @@ #include "device.h" //#define TSCH_DEFAULT_SLOT_DURATION_US 2024 -#define TSCH_DEFAULT_SLOT_DURATION_US 1000 * 20 +#define TSCH_DEFAULT_SLOT_DURATION_US 1000 * 2000 /* Very simple test schedule */ schedule_t schedule_test = { @@ -57,9 +57,9 @@ static void radio_callback(uint8_t *packet, uint8_t length); int main(void) { // initialize schedule - schedule_t schedule = schedule_test; + schedule_t schedule = schedule_minuscule; schedule.slot_duration_us = TSCH_DEFAULT_SLOT_DURATION_US; - node_type_t node_type = NODE_TYPE_GATEWAY; + node_type_t node_type = NODE_TYPE_DOTBOT; db_scheduler_init(node_type, &schedule); printf("Device of type %c and id %llx is using schedule %d\n\n", node_type, db_device_id(), schedule.id); From e81b2a91c71855d234e4550381835c323bd6d237 Mon Sep 17 00:00:00 2001 From: Geovane Fedrecheski Date: Tue, 7 Jan 2025 14:50:26 +0100 Subject: [PATCH 08/17] tsch: define intra-slot timing constants --- drv/scheduler.h | 1 - drv/scheduler/all_schedules.c | 9 --------- drv/scheduler/scheduler.c | 1 - drv/tsch.h | 24 ++++++++++++++++++++++- drv/tsch/tsch.c | 19 ++++++++++++++++-- projects/01drv_scheduler/test_schedules.c | 7 ------- projects/01drv_tsch/01drv_tsch.c | 6 +----- 7 files changed, 41 insertions(+), 26 deletions(-) diff --git a/drv/scheduler.h b/drv/scheduler.h index 736500179..20da66c16 100644 --- a/drv/scheduler.h +++ b/drv/scheduler.h @@ -58,7 +58,6 @@ typedef struct { uint8_t max_nodes; // maximum number of nodes that can be scheduled, equivalent to the number of uplink slots uint8_t backoff_n_min; // minimum exponent for the backoff algorithm uint8_t backoff_n_max; // maximum exponent for the backoff algorithm - uint32_t slot_duration_us; // duration of a slot in microseconds size_t n_cells; // number of cells in this schedule cell_t cells[TSCH_N_CELLS_MAX]; // cells in this schedule. NOTE(FIXME?): the first 3 cells must be beacons } schedule_t; diff --git a/drv/scheduler/all_schedules.c b/drv/scheduler/all_schedules.c index 31043904b..704edbe54 100644 --- a/drv/scheduler/all_schedules.c +++ b/drv/scheduler/all_schedules.c @@ -12,19 +12,12 @@ #define TSCH_N_SCHEDULES 1 + 2 // account for the schedule that can be passed by the application during initialization -#ifndef TSCH_DEFAULT_SLOT_DURATION_US -#define TSCH_DEFAULT_SLOT_DURATION_US 2024 -// #define TSCH_DEFAULT_SLOT_DURATION_US 1000 * 1000 -#endif - /* Schedule with 11 slots, supporting up to 5 nodes */ schedule_t schedule_minuscule = { .id = 6, .max_nodes = 5, .backoff_n_min = 5, .backoff_n_max = 9, - // .slot_duration_us = 2024, - .slot_duration_us = TSCH_DEFAULT_SLOT_DURATION_US, .n_cells = 11, .cells = { // Begin with beacon cells. They use their own channel offsets and frequencies. @@ -49,7 +42,6 @@ schedule_t schedule_tiny = { .max_nodes = 11, .backoff_n_min = 5, .backoff_n_max = 9, - .slot_duration_us = TSCH_DEFAULT_SLOT_DURATION_US, .n_cells = 17, .cells = { // Begin with beacon cells. They use their own channel offsets and frequencies. @@ -80,7 +72,6 @@ schedule_t schedule_small = { .max_nodes = 29, .backoff_n_min = 5, .backoff_n_max = 9, - .slot_duration_us = TSCH_DEFAULT_SLOT_DURATION_US, .n_cells = 41, .cells = { {'B', 0, NULL}, diff --git a/drv/scheduler/scheduler.c b/drv/scheduler/scheduler.c index 18a89c640..9319e38a5 100644 --- a/drv/scheduler/scheduler.c +++ b/drv/scheduler/scheduler.c @@ -122,7 +122,6 @@ tsch_radio_event_t db_scheduler_tick(void) { tsch_radio_event_t radio_event = { .radio_action = TSCH_RADIO_ACTION_SLEEP, - .duration_us = active_schedule.slot_duration_us, .frequency = db_scheduler_get_frequency(cell.type, _schedule_vars.asn, cell.channel_offset), .slot_type = cell.type, // FIXME: only for debugging, remove before merge }; diff --git a/drv/tsch.h b/drv/tsch.h index 667f7de19..010d57787 100644 --- a/drv/tsch.h +++ b/drv/tsch.h @@ -17,12 +17,23 @@ #include #include +#include "radio.h" + //=========================== defines ========================================== #define TSCH_TIMER_DEV 2 ///< HF timer device used for the TSCH scheduler #define TSCH_TIMER_SLOT_CHANNEL 0 #define TSCH_TIMER_INTRA_SLOT_CHANNEL 1 +// Bytes per millisecond in BLE 2M mode +#define BLE_2M (1000 * 1000 * 2) // 2 Mbps +#define BLE_2M_B_MS (BLE_2M / 8 / 1000) // 250 bytes/ms +#define BLE_2M_US_PER_BYTE (1000 / BLE_2M_B_MS) // 4 us + +#define _TSCH_START_GUARD_TIME (200) +#define _TSCH_PACKET_TOA (BLE_2M_US_PER_BYTE * DB_BLE_PAYLOAD_MAX_LENGTH) // Time on air for the maximum payload. +#define _TSCH_PACKET_TOA_WITH_PADDING (_TSCH_PACKET_TOA + (BLE_2M_US_PER_BYTE * 32)) // Add some padding just in case. + //=========================== variables ======================================== typedef void (*tsch_cb_t)(uint8_t *packet, uint8_t length); ///< Function pointer to the callback function called on packet receive @@ -33,6 +44,18 @@ typedef enum { TSCH_RADIO_ACTION_TX = 'T', } tsch_radio_action_t; +/* Timing of intra-slot sections */ +typedef struct { + uint32_t rx_offset; ///< Offset for the receiver to start receiving. + uint32_t rx_max; ///< Maximum time the receiver can be active. + uint32_t tx_offset; ///< Offset for the transmitter to start transmitting. Has to be bigger than rx_offset. + uint32_t tx_max; ///< Maximum time the transmitter can be active. Has to be smaller than rx_max. + uint32_t end_guard; ///< Time to wait after the end of the slot, so that the radio can fully turn off. Can be overriden with a large value to facilitate debugging. + uint32_t total_duration; ///< Total duration of the slot +} tsch_slot_timing_t; + +extern tsch_slot_timing_t tsch_default_slot_timing; + // ==== BEGIN TODO: move to protocol.h, but that will be part of a larger refactoring ==== typedef enum { TSCH_PACKET_TYPE_BEACON = 1, @@ -64,7 +87,6 @@ typedef struct { typedef struct { tsch_radio_action_t radio_action; uint8_t frequency; - uint32_t duration_us; char slot_type; // FIXME: only for debugging, remove before merge } tsch_radio_event_t; diff --git a/drv/tsch/tsch.c b/drv/tsch/tsch.c index ae19bff78..067684bce 100644 --- a/drv/tsch/tsch.c +++ b/drv/tsch/tsch.c @@ -34,6 +34,18 @@ typedef enum { // etc. } tsch_state_t; // TODO: actually use this state in the handlers below +tsch_slot_timing_t tsch_default_slot_timing = { + .rx_offset = 40 + 100, // Radio ramp-up time (40 us), + 100 us for any processing needed + .rx_max = _TSCH_START_GUARD_TIME + _TSCH_PACKET_TOA_WITH_PADDING, // Guard time + Enough time to receive the maximum payload. + .tx_offset = 40 + 100 + _TSCH_START_GUARD_TIME, // Same as rx_offset, plus the guard time. + .tx_max = _TSCH_PACKET_TOA_WITH_PADDING, // Enough to transmit the maximum payload. + .end_guard = 100, // Extra time at the end of the slot + + // receive slot is: rx_offset / rx_max / end_guard + // transmit slot is: tx_offset / tx_max / end_guard + .total_duration = 40 + 100 + _TSCH_START_GUARD_TIME + _TSCH_PACKET_TOA_WITH_PADDING + 100, // Total duration of the slot +}; + typedef struct { tsch_cb_t application_callback; ///< Function pointer, stores the application callback uint64_t device_id; ///< Device ID @@ -75,6 +87,9 @@ void db_tsch_init(tsch_cb_t application_callback) { // NOTE: assume the scheduler has already been initialized by the application + // set slot total duration + tsch_default_slot_timing.total_duration = tsch_default_slot_timing.rx_offset + tsch_default_slot_timing.rx_max + tsch_default_slot_timing.end_guard; + // start the ticking immediately db_timer_hf_set_oneshot_us(TSCH_TIMER_DEV, TSCH_TIMER_SLOT_CHANNEL, 100, _timer_tsch_slot_handler); } @@ -89,7 +104,7 @@ void _timer_tsch_slot_handler(void) { // FIXME: for some reason, it just receives (beacon) packets on frequency 26, and it is always "CRC error" tsch_radio_event_t event = db_scheduler_tick(); - printf("Event %c: %c, %d, %d\n", event.slot_type, event.radio_action, event.frequency, event.duration_us); // FIXME: only for debugging, remove before merge + printf("Event %c: %c, %d Slot duration: %d\n", event.slot_type, event.radio_action, event.frequency, tsch_default_slot_timing.total_duration); // FIXME: only for debugging, remove before merge switch (event.radio_action) { case TSCH_RADIO_ACTION_TX: @@ -113,7 +128,7 @@ void _timer_tsch_slot_handler(void) { // schedule the next tick // FIXME: compensate for the time spent in the instructions above. For now, just using 'event.duration_us' will do. - db_timer_hf_set_oneshot_us(TSCH_TIMER_DEV, TSCH_TIMER_SLOT_CHANNEL, event.duration_us, _timer_tsch_slot_handler); + db_timer_hf_set_oneshot_us(TSCH_TIMER_DEV, TSCH_TIMER_SLOT_CHANNEL, tsch_default_slot_timing.total_duration, _timer_tsch_slot_handler); } static void _tsch_callback(uint8_t *packet, uint8_t length) { diff --git a/projects/01drv_scheduler/test_schedules.c b/projects/01drv_scheduler/test_schedules.c index 43bcd6745..a8689a31d 100644 --- a/projects/01drv_scheduler/test_schedules.c +++ b/projects/01drv_scheduler/test_schedules.c @@ -1,16 +1,11 @@ #include "scheduler.h" -#define IS_DOTBOT -// #define TSCH_DEFAULT_SLOT_DURATION_US 2024 -#define TSCH_DEFAULT_SLOT_DURATION_US 1000 * 1000 - /* Very simple test schedule */ schedule_t schedule_test = { .id = 10, // make sure it doesn't collide .max_nodes = 2, .backoff_n_min = 5, .backoff_n_max = 9, - .slot_duration_us = TSCH_DEFAULT_SLOT_DURATION_US, .n_cells = 5, .cells = { // Only downlink slots @@ -28,7 +23,6 @@ schedule_t schedule_all_uplink = { .max_nodes = 2, .backoff_n_min = 5, .backoff_n_max = 9, - .slot_duration_us = TSCH_DEFAULT_SLOT_DURATION_US, .n_cells = 5, .cells = { // Only downlink slots @@ -46,7 +40,6 @@ schedule_t schedule_all_downlink = { .max_nodes = 2, .backoff_n_min = 5, .backoff_n_max = 9, - .slot_duration_us = TSCH_DEFAULT_SLOT_DURATION_US, .n_cells = 5, .cells = { // Only downlink slots diff --git a/projects/01drv_tsch/01drv_tsch.c b/projects/01drv_tsch/01drv_tsch.c index 6da4b9758..493a89430 100644 --- a/projects/01drv_tsch/01drv_tsch.c +++ b/projects/01drv_tsch/01drv_tsch.c @@ -19,16 +19,12 @@ #include "protocol.h" #include "device.h" -//#define TSCH_DEFAULT_SLOT_DURATION_US 2024 -#define TSCH_DEFAULT_SLOT_DURATION_US 1000 * 2000 - /* Very simple test schedule */ schedule_t schedule_test = { .id = 32, // make sure it doesn't collide .max_nodes = 0, .backoff_n_min = 5, .backoff_n_max = 9, - .slot_duration_us = TSCH_DEFAULT_SLOT_DURATION_US, .n_cells = 5, .cells = { //{'B', 0, NULL}, @@ -58,12 +54,12 @@ static void radio_callback(uint8_t *packet, uint8_t length); int main(void) { // initialize schedule schedule_t schedule = schedule_minuscule; - schedule.slot_duration_us = TSCH_DEFAULT_SLOT_DURATION_US; node_type_t node_type = NODE_TYPE_DOTBOT; db_scheduler_init(node_type, &schedule); printf("Device of type %c and id %llx is using schedule %d\n\n", node_type, db_device_id(), schedule.id); // initialize the TSCH driver + tsch_default_slot_timing.end_guard = 1000 * 1000; // add an extra second of delay. db_tsch_init(radio_callback); while (1) { From ce8cd8e452c661bc1b4d6e88a89b75bd6d51d740 Mon Sep 17 00:00:00 2001 From: Geovane Fedrecheski Date: Tue, 7 Jan 2025 17:36:03 +0100 Subject: [PATCH 09/17] tsch: state machine with intra-slot timers work --- drv/scheduler.h | 7 +- drv/scheduler/scheduler.c | 2 + drv/tsch.h | 7 +- drv/tsch/tsch.c | 198 ++++++++++++++++++++++++++----- projects/01drv_tsch/01drv_tsch.c | 4 +- 5 files changed, 181 insertions(+), 37 deletions(-) diff --git a/drv/scheduler.h b/drv/scheduler.h index 20da66c16..aa8a9bb42 100644 --- a/drv/scheduler.h +++ b/drv/scheduler.h @@ -33,12 +33,9 @@ #define TSCH_N_CELLS_MAX 101 -//=========================== variables ======================================== +// #define TSCH_LISTEN_DURING_UNSCHEDULED_UPLINK 1 -typedef enum { - NODE_TYPE_GATEWAY = 'G', - NODE_TYPE_DOTBOT = 'D', -} node_type_t; +//=========================== variables ======================================== typedef enum { SLOT_TYPE_BEACON = 'B', diff --git a/drv/scheduler/scheduler.c b/drv/scheduler/scheduler.c index 9319e38a5..842d94ad4 100644 --- a/drv/scheduler/scheduler.c +++ b/drv/scheduler/scheduler.c @@ -185,11 +185,13 @@ void _compute_dotbot_action(cell_t cell, tsch_radio_event_t *radio_event) { if (cell.assigned_node_id == db_device_id()) { radio_event->radio_action = TSCH_RADIO_ACTION_TX; } else { +#ifdef TSCH_LISTEN_DURING_UNSCHEDULED_UPLINK // OPTIMIZATION: listen for beacons during unassigned uplink slot // listen to the same beacon frequency for a whole slotframe radio_event->radio_action = TSCH_RADIO_ACTION_RX; size_t beacon_channel = TSCH_N_BLE_REGULAR_FREQUENCIES + (_schedule_vars.slotframe_counter % TSCH_N_BLE_ADVERTISING_FREQUENCIES); radio_event->frequency = _ble_chan_to_freq[beacon_channel]; +#endif } break; default: diff --git a/drv/tsch.h b/drv/tsch.h index 010d57787..e295c0a75 100644 --- a/drv/tsch.h +++ b/drv/tsch.h @@ -36,7 +36,10 @@ //=========================== variables ======================================== -typedef void (*tsch_cb_t)(uint8_t *packet, uint8_t length); ///< Function pointer to the callback function called on packet receive +typedef enum { + NODE_TYPE_GATEWAY = 'G', + NODE_TYPE_DOTBOT = 'D', +} node_type_t; typedef enum { TSCH_RADIO_ACTION_SLEEP = 'S', @@ -90,6 +93,8 @@ typedef struct { char slot_type; // FIXME: only for debugging, remove before merge } tsch_radio_event_t; +typedef void (*tsch_cb_t)(uint8_t *packet, uint8_t length); ///< Function pointer to the callback function called on packet receive + //=========================== prototypes ========================================== /** diff --git a/drv/tsch/tsch.c b/drv/tsch/tsch.c index 067684bce..9b9da2c0e 100644 --- a/drv/tsch/tsch.c +++ b/drv/tsch/tsch.c @@ -27,17 +27,10 @@ //=========================== variables ======================================== -typedef enum { - TSCH_STATE_WAIT_TO_RX, - TSCH_STATE_STARTED_RX, - TSCH_STATE_DONE_RX, - // etc. -} tsch_state_t; // TODO: actually use this state in the handlers below - tsch_slot_timing_t tsch_default_slot_timing = { - .rx_offset = 40 + 100, // Radio ramp-up time (40 us), + 100 us for any processing needed + .rx_offset = 40 + 300, // Radio ramp-up time (40 us), + 100 us for any processing needed .rx_max = _TSCH_START_GUARD_TIME + _TSCH_PACKET_TOA_WITH_PADDING, // Guard time + Enough time to receive the maximum payload. - .tx_offset = 40 + 100 + _TSCH_START_GUARD_TIME, // Same as rx_offset, plus the guard time. + .tx_offset = 40 + 300 + _TSCH_START_GUARD_TIME, // Same as rx_offset, plus the guard time. .tx_max = _TSCH_PACKET_TOA_WITH_PADDING, // Enough to transmit the maximum payload. .end_guard = 100, // Extra time at the end of the slot @@ -46,10 +39,33 @@ tsch_slot_timing_t tsch_default_slot_timing = { .total_duration = 40 + 100 + _TSCH_START_GUARD_TIME + _TSCH_PACKET_TOA_WITH_PADDING + 100, // Total duration of the slot }; +typedef enum { + // receiver, + TSCH_STATE_WAIT_RX_OFFSET, + TSCH_STATE_DO_RX, + TSCH_STATE_IS_RXING, + + // transmitter, + TSCH_STATE_WAIT_TX_OFFSET, + TSCH_STATE_DO_TX, + TSCH_STATE_IS_TXING, + + // common, + TSCH_STATE_BEGIN_SLOT, + TSCH_STATE_IS_END_GUARD, +} tsch_state_t; // TODO: actually use this state in the handlers below + typedef struct { - tsch_cb_t application_callback; ///< Function pointer, stores the application callback + node_type_t node_type; // whether the node is a gateway or a dotbot uint64_t device_id; ///< Device ID + tsch_state_t state; ///< State of the TSCH state machine + tsch_radio_event_t event; ///< Current event to process + + uint8_t packet[DB_BLE_PAYLOAD_MAX_LENGTH]; ///< Buffer to store the packet to transmit + size_t packet_len; ///< Length of the packet to transmit + + tsch_cb_t application_callback; ///< Function pointer, stores the application callback } tsch_vars_t; static tsch_vars_t _tsch_vars = { 0 }; @@ -61,16 +77,30 @@ uint8_t default_packet[] = { //========================== prototypes ======================================== -/** - * @brief Interrupt handler for the slot-wise ticking of the TSCH scheduler. - */ -static void _timer_tsch_slot_handler(void); +// /** +// * @brief Interrupt handler for the slot-wise ticking of the TSCH scheduler. +// */ +// static void _timer_tsch_slot_handler(void); /* * @brief Callback function passed to the radio driver. */ static void _tsch_callback(uint8_t *packet, uint8_t length); +static void _tsch_sm_handler(void); +static void _handler_sm_begin_slot(void); +static void _handler_sm_do_rx(void); +static void _handler_sm_is_rxing(void); +static void _handler_sm_do_tx(void); +static void _handler_sm_is_txing(void); + +static inline void _set_timer_and_compensate(uint32_t duration, uint32_t start_ts, timer_hf_cb_t cb); + +/* +* @brief Set TSCH state machine state, to be used after the timer expires. +*/ +static inline void _set_state_for_after_timer(tsch_state_t state); + //=========================== public =========================================== void db_tsch_init(tsch_cb_t application_callback) { @@ -91,44 +121,139 @@ void db_tsch_init(tsch_cb_t application_callback) { tsch_default_slot_timing.total_duration = tsch_default_slot_timing.rx_offset + tsch_default_slot_timing.rx_max + tsch_default_slot_timing.end_guard; // start the ticking immediately - db_timer_hf_set_oneshot_us(TSCH_TIMER_DEV, TSCH_TIMER_SLOT_CHANNEL, 100, _timer_tsch_slot_handler); + // db_timer_hf_set_oneshot_us(TSCH_TIMER_DEV, TSCH_TIMER_SLOT_CHANNEL, 100, _timer_tsch_slot_handler); + + // initialize and start the state machine + _set_state_for_after_timer(TSCH_STATE_BEGIN_SLOT); + db_timer_hf_set_oneshot_us(TSCH_TIMER_DEV, TSCH_TIMER_SLOT_CHANNEL, 100, _tsch_sm_handler); } //=========================== private ========================================== +// state machine handler +void _tsch_sm_handler(void) { + printf("State: %d\n", _tsch_vars.state); -// TODO: manipulate the _tsch_vars.state - + switch (_tsch_vars.state) { + case TSCH_STATE_BEGIN_SLOT: + _handler_sm_begin_slot(); + break; + case TSCH_STATE_DO_RX: + _handler_sm_do_rx(); + break; + case TSCH_STATE_IS_RXING: + _handler_sm_is_rxing(); + break; + case TSCH_STATE_DO_TX: + _handler_sm_do_tx(); + break; + case TSCH_STATE_IS_TXING: + _handler_sm_is_txing(); + break; + default: + break; + } +} -void _timer_tsch_slot_handler(void) { - // FIXME: for some reason, it just receives (beacon) packets on frequency 26, and it is always "CRC error" +void _handler_sm_begin_slot(void) { + uint32_t start_ts = db_timer_hf_now(TSCH_TIMER_DEV); + uint32_t timer_duration = 0; tsch_radio_event_t event = db_scheduler_tick(); - printf("Event %c: %c, %d Slot duration: %d\n", event.slot_type, event.radio_action, event.frequency, tsch_default_slot_timing.total_duration); // FIXME: only for debugging, remove before merge + // printf("(_handler_sm_begin_slot) Event %c: %c, %d Slot duration: %d\n", event.slot_type, event.radio_action, event.frequency, tsch_default_slot_timing.total_duration); switch (event.radio_action) { case TSCH_RADIO_ACTION_TX: + // configure radio db_radio_disable(); db_radio_set_frequency(event.frequency); - // TODO: send a real packet: - // - from the application queue - // - a beacon, if it is a beacon slot and the node is a gateway - db_radio_tx(default_packet, sizeof(default_packet)); + + // update state + _tsch_vars.event = event; + _set_state_for_after_timer(TSCH_STATE_DO_TX); + + // get the packet to tx and save in _tsch_vars + // TODO: how to get a packet? decide based on _tsch_vars.node_type and event.slot_type + // could the event come with a packet? sometimes maybe? or would it be confusing? + _tsch_vars.packet_len = sizeof(default_packet); + memcpy(_tsch_vars.packet, default_packet, _tsch_vars.packet_len); + + // set timer duration to resume again after tx_offset + timer_duration = tsch_default_slot_timing.tx_offset; break; case TSCH_RADIO_ACTION_RX: - // TODO: if this is a DotBot, implement re-synchronization logic + // configure radio db_radio_disable(); db_radio_set_frequency(event.frequency); - db_radio_rx(); + + // update state + _tsch_vars.event = event; + _set_state_for_after_timer(TSCH_STATE_DO_RX); + + // set timer duration to resume again after rx_offset + timer_duration = tsch_default_slot_timing.rx_offset; break; case TSCH_RADIO_ACTION_SLEEP: + // just disable the radio and do nothing, then come back for the next slot db_radio_disable(); + _set_state_for_after_timer(TSCH_STATE_BEGIN_SLOT); // keep the same state + timer_duration = tsch_default_slot_timing.total_duration; break; } - // schedule the next tick - // FIXME: compensate for the time spent in the instructions above. For now, just using 'event.duration_us' will do. - db_timer_hf_set_oneshot_us(TSCH_TIMER_DEV, TSCH_TIMER_SLOT_CHANNEL, tsch_default_slot_timing.total_duration, _timer_tsch_slot_handler); + _set_timer_and_compensate(timer_duration, start_ts, &_tsch_sm_handler); +} + +// --- rx handlers --- +void _handler_sm_do_rx(void) { + uint32_t start_ts = db_timer_hf_now(TSCH_TIMER_DEV); + + // update state + _set_state_for_after_timer(TSCH_STATE_IS_RXING); + + // receive packets + db_radio_rx(); // remember: this always starts with a guard time before the actual transmission begins + + _set_timer_and_compensate(tsch_default_slot_timing.rx_max, start_ts, &_tsch_sm_handler); +} +void _handler_sm_is_rxing(void) { + // just disable the radio and do nothing, then come back for the next slot + + uint32_t start_ts = db_timer_hf_now(TSCH_TIMER_DEV); + + // configure radio + db_radio_disable(); + + // set state for after the timer expires + _set_state_for_after_timer(TSCH_STATE_BEGIN_SLOT); + + _set_timer_and_compensate(tsch_default_slot_timing.end_guard, start_ts, &_tsch_sm_handler); +} + +// --- tx handlers --- +void _handler_sm_do_tx(void) { + uint32_t start_ts = db_timer_hf_now(TSCH_TIMER_DEV); + + // update state + _set_state_for_after_timer(TSCH_STATE_IS_TXING); + + // send the packet + db_radio_tx(_tsch_vars.packet, _tsch_vars.packet_len); + + _set_timer_and_compensate(tsch_default_slot_timing.tx_max, start_ts, &_tsch_sm_handler); +} +void _handler_sm_is_txing(void) { + // just disable the radio and do nothing, then come back for the next slot + + uint32_t start_ts = db_timer_hf_now(TSCH_TIMER_DEV); + + // configure radio + db_radio_disable(); + + // set state for after the timer expires + _set_state_for_after_timer(TSCH_STATE_BEGIN_SLOT); + + _set_timer_and_compensate(tsch_default_slot_timing.end_guard, start_ts, &_tsch_sm_handler); } static void _tsch_callback(uint8_t *packet, uint8_t length) { @@ -146,3 +271,18 @@ static void _tsch_callback(uint8_t *packet, uint8_t length) { _tsch_vars.application_callback(packet, length); } } + +static inline void _set_state_for_after_timer(tsch_state_t state) { + _tsch_vars.state = state; +} + +static inline void _set_timer_and_compensate(uint32_t duration, uint32_t start_ts, timer_hf_cb_t timer_callback) { + uint32_t elapsed_ts = db_timer_hf_now(TSCH_TIMER_DEV) - start_ts; + printf("Setting timer for duration %d, compensating for elapsed %d gives: %d\n", duration, elapsed_ts, duration - elapsed_ts); + db_timer_hf_set_oneshot_us( + TSCH_TIMER_DEV, + TSCH_TIMER_SLOT_CHANNEL, + duration - elapsed_ts, + timer_callback + ); +} diff --git a/projects/01drv_tsch/01drv_tsch.c b/projects/01drv_tsch/01drv_tsch.c index 493a89430..91a200a5d 100644 --- a/projects/01drv_tsch/01drv_tsch.c +++ b/projects/01drv_tsch/01drv_tsch.c @@ -33,7 +33,7 @@ schedule_t schedule_test = { //{'U', 3, NULL}, //{'U', 4, NULL}, - {'B', 0, NULL}, + {'S', 0, NULL}, {'B', 1, NULL}, {'B', 2, NULL}, {'B', 3, NULL}, @@ -53,7 +53,7 @@ static void radio_callback(uint8_t *packet, uint8_t length); int main(void) { // initialize schedule - schedule_t schedule = schedule_minuscule; + schedule_t schedule = schedule_test; node_type_t node_type = NODE_TYPE_DOTBOT; db_scheduler_init(node_type, &schedule); printf("Device of type %c and id %llx is using schedule %d\n\n", node_type, db_device_id(), schedule.id); From 431dcf8eef2adb7274abe822ed8e89adf1aadda1 Mon Sep 17 00:00:00 2001 From: Geovane Fedrecheski Date: Wed, 8 Jan 2025 10:59:19 +0100 Subject: [PATCH 10/17] tsch: instrument with gpio for debug --- drv/tsch/tsch.c | 70 ++++++++++++++++++++++---------- projects/01drv_tsch/01drv_tsch.c | 3 +- 2 files changed, 50 insertions(+), 23 deletions(-) diff --git a/drv/tsch/tsch.c b/drv/tsch/tsch.c index 9b9da2c0e..d68bd9395 100644 --- a/drv/tsch/tsch.c +++ b/drv/tsch/tsch.c @@ -23,6 +23,19 @@ #if defined(NRF5340_XXAA) && defined(NRF_NETWORK) #include "ipc.h" #endif + +#ifndef DEBUG // FIXME: remove before merge. Just to make VS Code enable code behind `#ifdef DEBUG` +#define DEBUG +#endif + +#ifdef DEBUG // comes from segger config +#include "gpio.h" // for debugging +gpio_t pin0 = { .port = 1, .pin = 2 }; // variable names reflect the logic analyzer channels +gpio_t pin1 = { .port = 1, .pin = 3 }; +gpio_t pin2 = { .port = 1, .pin = 4 }; +gpio_t pin3 = { .port = 1, .pin = 5 }; +#endif + //=========================== defines ========================================== //=========================== variables ======================================== @@ -40,19 +53,19 @@ tsch_slot_timing_t tsch_default_slot_timing = { }; typedef enum { + // common, + TSCH_STATE_BEGIN_SLOT, + // receiver, - TSCH_STATE_WAIT_RX_OFFSET, - TSCH_STATE_DO_RX, - TSCH_STATE_IS_RXING, + TSCH_STATE_WAIT_RX_OFFSET = 11, + TSCH_STATE_DO_RX = 12, + TSCH_STATE_IS_RXING = 13, // transmitter, - TSCH_STATE_WAIT_TX_OFFSET, - TSCH_STATE_DO_TX, - TSCH_STATE_IS_TXING, + TSCH_STATE_WAIT_TX_OFFSET = 20, + TSCH_STATE_DO_TX = 21, + TSCH_STATE_IS_TXING = 22, - // common, - TSCH_STATE_BEGIN_SLOT, - TSCH_STATE_IS_END_GUARD, } tsch_state_t; // TODO: actually use this state in the handlers below typedef struct { @@ -99,11 +112,18 @@ static inline void _set_timer_and_compensate(uint32_t duration, uint32_t start_t /* * @brief Set TSCH state machine state, to be used after the timer expires. */ -static inline void _set_state_for_after_timer(tsch_state_t state); +static inline void _set_next_state(tsch_state_t state); //=========================== public =========================================== void db_tsch_init(tsch_cb_t application_callback) { +#ifdef DEBUG + db_gpio_init(&pin0, DB_GPIO_OUT); + db_gpio_init(&pin1, DB_GPIO_OUT); + db_gpio_init(&pin2, DB_GPIO_OUT); + db_gpio_init(&pin3, DB_GPIO_OUT); +#endif + // initialize the high frequency clock db_timer_hf_init(TSCH_TIMER_DEV); @@ -124,7 +144,7 @@ void db_tsch_init(tsch_cb_t application_callback) { // db_timer_hf_set_oneshot_us(TSCH_TIMER_DEV, TSCH_TIMER_SLOT_CHANNEL, 100, _timer_tsch_slot_handler); // initialize and start the state machine - _set_state_for_after_timer(TSCH_STATE_BEGIN_SLOT); + _set_next_state(TSCH_STATE_BEGIN_SLOT); db_timer_hf_set_oneshot_us(TSCH_TIMER_DEV, TSCH_TIMER_SLOT_CHANNEL, 100, _tsch_sm_handler); } @@ -132,7 +152,7 @@ void db_tsch_init(tsch_cb_t application_callback) { // state machine handler void _tsch_sm_handler(void) { - printf("State: %d\n", _tsch_vars.state); + // printf("State: %d\n", _tsch_vars.state); switch (_tsch_vars.state) { case TSCH_STATE_BEGIN_SLOT: @@ -156,10 +176,16 @@ void _tsch_sm_handler(void) { } void _handler_sm_begin_slot(void) { +#ifdef DEBUG + db_gpio_toggle(&pin0); +#endif uint32_t start_ts = db_timer_hf_now(TSCH_TIMER_DEV); + uint32_t timer_duration = 0; + // uint32_t start_sched_ts = db_timer_hf_now(TSCH_TIMER_DEV); tsch_radio_event_t event = db_scheduler_tick(); + // printf("Scheduler tick took %d us\n", db_timer_hf_now(TSCH_TIMER_DEV) - start_sched_ts); // printf("(_handler_sm_begin_slot) Event %c: %c, %d Slot duration: %d\n", event.slot_type, event.radio_action, event.frequency, tsch_default_slot_timing.total_duration); switch (event.radio_action) { @@ -170,7 +196,7 @@ void _handler_sm_begin_slot(void) { // update state _tsch_vars.event = event; - _set_state_for_after_timer(TSCH_STATE_DO_TX); + _set_next_state(TSCH_STATE_DO_TX); // get the packet to tx and save in _tsch_vars // TODO: how to get a packet? decide based on _tsch_vars.node_type and event.slot_type @@ -188,7 +214,7 @@ void _handler_sm_begin_slot(void) { // update state _tsch_vars.event = event; - _set_state_for_after_timer(TSCH_STATE_DO_RX); + _set_next_state(TSCH_STATE_DO_RX); // set timer duration to resume again after rx_offset timer_duration = tsch_default_slot_timing.rx_offset; @@ -196,7 +222,7 @@ void _handler_sm_begin_slot(void) { case TSCH_RADIO_ACTION_SLEEP: // just disable the radio and do nothing, then come back for the next slot db_radio_disable(); - _set_state_for_after_timer(TSCH_STATE_BEGIN_SLOT); // keep the same state + _set_next_state(TSCH_STATE_BEGIN_SLOT); // keep the same state timer_duration = tsch_default_slot_timing.total_duration; break; } @@ -209,7 +235,7 @@ void _handler_sm_do_rx(void) { uint32_t start_ts = db_timer_hf_now(TSCH_TIMER_DEV); // update state - _set_state_for_after_timer(TSCH_STATE_IS_RXING); + _set_next_state(TSCH_STATE_IS_RXING); // receive packets db_radio_rx(); // remember: this always starts with a guard time before the actual transmission begins @@ -225,7 +251,7 @@ void _handler_sm_is_rxing(void) { db_radio_disable(); // set state for after the timer expires - _set_state_for_after_timer(TSCH_STATE_BEGIN_SLOT); + _set_next_state(TSCH_STATE_BEGIN_SLOT); _set_timer_and_compensate(tsch_default_slot_timing.end_guard, start_ts, &_tsch_sm_handler); } @@ -235,7 +261,7 @@ void _handler_sm_do_tx(void) { uint32_t start_ts = db_timer_hf_now(TSCH_TIMER_DEV); // update state - _set_state_for_after_timer(TSCH_STATE_IS_TXING); + _set_next_state(TSCH_STATE_IS_TXING); // send the packet db_radio_tx(_tsch_vars.packet, _tsch_vars.packet_len); @@ -251,13 +277,13 @@ void _handler_sm_is_txing(void) { db_radio_disable(); // set state for after the timer expires - _set_state_for_after_timer(TSCH_STATE_BEGIN_SLOT); + _set_next_state(TSCH_STATE_BEGIN_SLOT); _set_timer_and_compensate(tsch_default_slot_timing.end_guard, start_ts, &_tsch_sm_handler); } static void _tsch_callback(uint8_t *packet, uint8_t length) { - printf("Received packet of length %d\n", length); + // printf("Received packet of length %d\n", length); // Check if the packet is big enough to have a header if (length < sizeof(protocol_header_t)) { @@ -272,13 +298,13 @@ static void _tsch_callback(uint8_t *packet, uint8_t length) { } } -static inline void _set_state_for_after_timer(tsch_state_t state) { +static inline void _set_next_state(tsch_state_t state) { _tsch_vars.state = state; } static inline void _set_timer_and_compensate(uint32_t duration, uint32_t start_ts, timer_hf_cb_t timer_callback) { uint32_t elapsed_ts = db_timer_hf_now(TSCH_TIMER_DEV) - start_ts; - printf("Setting timer for duration %d, compensating for elapsed %d gives: %d\n", duration, elapsed_ts, duration - elapsed_ts); + // printf("Setting timer for duration %d, compensating for elapsed %d gives: %d\n", duration, elapsed_ts, duration - elapsed_ts); db_timer_hf_set_oneshot_us( TSCH_TIMER_DEV, TSCH_TIMER_SLOT_CHANNEL, diff --git a/projects/01drv_tsch/01drv_tsch.c b/projects/01drv_tsch/01drv_tsch.c index 91a200a5d..04e22ab9f 100644 --- a/projects/01drv_tsch/01drv_tsch.c +++ b/projects/01drv_tsch/01drv_tsch.c @@ -59,8 +59,9 @@ int main(void) { printf("Device of type %c and id %llx is using schedule %d\n\n", node_type, db_device_id(), schedule.id); // initialize the TSCH driver - tsch_default_slot_timing.end_guard = 1000 * 1000; // add an extra second of delay. + //tsch_default_slot_timing.end_guard = 1000 * 1000; // add an extra second of delay. db_tsch_init(radio_callback); + printf("Slot total duration: %d us\n", tsch_default_slot_timing.total_duration); while (1) { __WFE(); From a3a61e2c0ea1253083320f982c8db51cee093d81 Mon Sep 17 00:00:00 2001 From: Geovane Fedrecheski Date: Wed, 8 Jan 2025 15:59:26 +0100 Subject: [PATCH 11/17] tsch: dedicated timer channels for slot and intra-slot --- drv/scheduler.h | 3 +- drv/scheduler/scheduler.c | 11 +- drv/tsch.h | 10 +- drv/tsch/tsch.c | 192 ++++++++++----------- projects/01drv_scheduler/01drv_scheduler.c | 9 +- projects/01drv_tsch/01drv_tsch.c | 4 +- 6 files changed, 111 insertions(+), 118 deletions(-) diff --git a/drv/scheduler.h b/drv/scheduler.h index aa8a9bb42..396c0391b 100644 --- a/drv/scheduler.h +++ b/drv/scheduler.h @@ -75,7 +75,8 @@ void db_scheduler_init(node_type_t node_type, schedule_t *application_schedule); * * @return A configuration for the TSCH radio driver to follow in the next slot. */ -tsch_radio_event_t db_scheduler_tick(void); +// tsch_radio_event_t db_scheduler_tick(void); +tsch_radio_event_t db_scheduler_tick(uint64_t asn); /** * @brief Activates a given schedule. diff --git a/drv/scheduler/scheduler.c b/drv/scheduler/scheduler.c index 842d94ad4..f944e1cb8 100644 --- a/drv/scheduler/scheduler.c +++ b/drv/scheduler/scheduler.c @@ -113,16 +113,16 @@ bool db_scheduler_deassign_uplink_cell(uint64_t node_id) { return false; } -tsch_radio_event_t db_scheduler_tick(void) { +tsch_radio_event_t db_scheduler_tick(uint64_t asn) { schedule_t active_schedule = *_schedule_vars.active_schedule_ptr; // get the current cell - size_t cell_index = _schedule_vars.asn % active_schedule.n_cells; + size_t cell_index = asn % active_schedule.n_cells; cell_t cell = active_schedule.cells[cell_index]; tsch_radio_event_t radio_event = { .radio_action = TSCH_RADIO_ACTION_SLEEP, - .frequency = db_scheduler_get_frequency(cell.type, _schedule_vars.asn, cell.channel_offset), + .frequency = db_scheduler_get_frequency(cell.type, asn, cell.channel_offset), .slot_type = cell.type, // FIXME: only for debugging, remove before merge }; if (_schedule_vars.node_type == NODE_TYPE_GATEWAY) { @@ -132,13 +132,10 @@ tsch_radio_event_t db_scheduler_tick(void) { } // if the slotframe wrapped, keep track of how many slotframes have passed (used to cycle beacon frequencies) - if (_schedule_vars.asn != 0 && cell_index == 0) { + if (asn != 0 && cell_index == 0) { _schedule_vars.slotframe_counter++; } - // increment ASN so that (1) nodes are in sync and (2) next time we get the next cell - _schedule_vars.asn++; - return radio_event; } diff --git a/drv/tsch.h b/drv/tsch.h index e295c0a75..cd1646bc5 100644 --- a/drv/tsch.h +++ b/drv/tsch.h @@ -22,8 +22,9 @@ //=========================== defines ========================================== #define TSCH_TIMER_DEV 2 ///< HF timer device used for the TSCH scheduler -#define TSCH_TIMER_SLOT_CHANNEL 0 -#define TSCH_TIMER_INTRA_SLOT_CHANNEL 1 +#define TSCH_TIMER_INTER_SLOT_CHANNEL 0 ///< Channel for ticking the whole slot +#define TSCH_TIMER_INTRA_SLOT_CHANNEL 1 ///< Channel for ticking intra-slot sections, such as tx offset and tx max +#define TSCH_TIMER_DESYNC_WINDOW_CHANNEL 2 ///< Channel for ticking the desynchronization window // Bytes per millisecond in BLE 2M mode #define BLE_2M (1000 * 1000 * 2) // 2 Mbps @@ -31,6 +32,7 @@ #define BLE_2M_US_PER_BYTE (1000 / BLE_2M_B_MS) // 4 us #define _TSCH_START_GUARD_TIME (200) +#define _TSCH_END_GUARD_TIME (100) #define _TSCH_PACKET_TOA (BLE_2M_US_PER_BYTE * DB_BLE_PAYLOAD_MAX_LENGTH) // Time on air for the maximum payload. #define _TSCH_PACKET_TOA_WITH_PADDING (_TSCH_PACKET_TOA + (BLE_2M_US_PER_BYTE * 32)) // Add some padding just in case. @@ -80,10 +82,10 @@ typedef struct { typedef struct { uint8_t version; tsch_packet_type_t type; + uint64_t asn; uint64_t src; uint8_t remaining_capacity; uint8_t active_schedule_id; - uint64_t asn; } tsch_beacon_packet_header_t; // ==== END TODO: move to protocol.h, but that will be part of a larger refactoring ====== @@ -103,6 +105,6 @@ typedef void (*tsch_cb_t)(uint8_t *packet, uint8_t length); ///< Function point * @param[in] callback pointer to a function that will be called each time a packet is received. * */ -void db_tsch_init(tsch_cb_t callback); +void db_tsch_init(node_type_t node_type, tsch_cb_t application_callback); #endif diff --git a/drv/tsch/tsch.c b/drv/tsch/tsch.c index d68bd9395..40844c071 100644 --- a/drv/tsch/tsch.c +++ b/drv/tsch/tsch.c @@ -36,6 +36,16 @@ gpio_t pin2 = { .port = 1, .pin = 4 }; gpio_t pin3 = { .port = 1, .pin = 5 }; #endif +#ifdef DEBUG + #define DEBUG_GPIO_TOGGLE(pin) db_gpio_toggle(pin) + #define DEBUG_GPIO_SET(pin) db_gpio_set(pin) + #define DEBUG_GPIO_CLEAR(pin) db_gpio_clear(pin) +#else + // No-op when DEBUG is not defined + #define DEBUG_GPIO_TOGGLE(pin) ((void)0)) + #define DEBUG_GPIO_SET(pin) ((void)0)) +#endif + //=========================== defines ========================================== //=========================== variables ======================================== @@ -45,11 +55,11 @@ tsch_slot_timing_t tsch_default_slot_timing = { .rx_max = _TSCH_START_GUARD_TIME + _TSCH_PACKET_TOA_WITH_PADDING, // Guard time + Enough time to receive the maximum payload. .tx_offset = 40 + 300 + _TSCH_START_GUARD_TIME, // Same as rx_offset, plus the guard time. .tx_max = _TSCH_PACKET_TOA_WITH_PADDING, // Enough to transmit the maximum payload. - .end_guard = 100, // Extra time at the end of the slot + .end_guard = _TSCH_END_GUARD_TIME, // Extra time at the end of the slot // receive slot is: rx_offset / rx_max / end_guard // transmit slot is: tx_offset / tx_max / end_guard - .total_duration = 40 + 100 + _TSCH_START_GUARD_TIME + _TSCH_PACKET_TOA_WITH_PADDING + 100, // Total duration of the slot + .total_duration = 40 + 100 + _TSCH_START_GUARD_TIME + _TSCH_PACKET_TOA_WITH_PADDING + _TSCH_END_GUARD_TIME, // Total duration of the slot }; typedef enum { @@ -72,6 +82,8 @@ typedef struct { node_type_t node_type; // whether the node is a gateway or a dotbot uint64_t device_id; ///< Device ID + uint64_t asn; ///< Absolute slot number + tsch_state_t state; ///< State of the TSCH state machine tsch_radio_event_t event; ///< Current event to process @@ -90,33 +102,30 @@ uint8_t default_packet[] = { //========================== prototypes ======================================== -// /** -// * @brief Interrupt handler for the slot-wise ticking of the TSCH scheduler. -// */ -// static void _timer_tsch_slot_handler(void); - -/* -* @brief Callback function passed to the radio driver. -*/ -static void _tsch_callback(uint8_t *packet, uint8_t length); +//-------------------------- state machine handlers ---------------------------- -static void _tsch_sm_handler(void); +static void _tsch_state_machine_handler(void); static void _handler_sm_begin_slot(void); -static void _handler_sm_do_rx(void); -static void _handler_sm_is_rxing(void); -static void _handler_sm_do_tx(void); -static void _handler_sm_is_txing(void); - -static inline void _set_timer_and_compensate(uint32_t duration, uint32_t start_ts, timer_hf_cb_t cb); /* * @brief Set TSCH state machine state, to be used after the timer expires. */ static inline void _set_next_state(tsch_state_t state); +// ------------------------- other functions ------------------------------------ + +/* +* @brief Callback function passed to the radio driver. +*/ +static void _tsch_callback(uint8_t *packet, uint8_t length); + +static inline void _set_timer_and_compensate(uint8_t channel, uint32_t duration, uint32_t start_ts, timer_hf_cb_t cb); +// static inline void _set_timer(uint8_t channel, uint32_t duration, timer_hf_cb_t timer_callback); + + //=========================== public =========================================== -void db_tsch_init(tsch_cb_t application_callback) { +void db_tsch_init(node_type_t node_type, tsch_cb_t application_callback) { #ifdef DEBUG db_gpio_init(&pin0, DB_GPIO_OUT); db_gpio_init(&pin1, DB_GPIO_OUT); @@ -133,42 +142,64 @@ void db_tsch_init(tsch_cb_t application_callback) { // Save the application callback to use in our interruption _tsch_vars.application_callback = application_callback; + // set node information + _tsch_vars.node_type = node_type; _tsch_vars.device_id = db_device_id(); + _tsch_vars.asn = 0; + // NOTE: assume the scheduler has already been initialized by the application // set slot total duration tsch_default_slot_timing.total_duration = tsch_default_slot_timing.rx_offset + tsch_default_slot_timing.rx_max + tsch_default_slot_timing.end_guard; - // start the ticking immediately - // db_timer_hf_set_oneshot_us(TSCH_TIMER_DEV, TSCH_TIMER_SLOT_CHANNEL, 100, _timer_tsch_slot_handler); - // initialize and start the state machine _set_next_state(TSCH_STATE_BEGIN_SLOT); - db_timer_hf_set_oneshot_us(TSCH_TIMER_DEV, TSCH_TIMER_SLOT_CHANNEL, 100, _tsch_sm_handler); + uint32_t time_padding = 10; // account for function call and interrupt latency + db_timer_hf_set_oneshot_us(TSCH_TIMER_DEV, TSCH_TIMER_INTER_SLOT_CHANNEL, time_padding, _tsch_state_machine_handler); // trigger the state machine } //=========================== private ========================================== // state machine handler -void _tsch_sm_handler(void) { +void _tsch_state_machine_handler(void) { + uint32_t start_ts = db_timer_hf_now(TSCH_TIMER_DEV); // printf("State: %d\n", _tsch_vars.state); switch (_tsch_vars.state) { case TSCH_STATE_BEGIN_SLOT: + DEBUG_GPIO_TOGGLE(&pin0); + DEBUG_GPIO_SET(&pin1); + // set the timer for the next slot TOTAL DURATION + // _set_timer(TSCH_TIMER_INTER_SLOT_CHANNEL, tsch_default_slot_timing.total_duration, &_tsch_state_machine_handler); + _set_timer_and_compensate(TSCH_TIMER_INTER_SLOT_CHANNEL, tsch_default_slot_timing.total_duration, start_ts, &_tsch_state_machine_handler); _handler_sm_begin_slot(); break; case TSCH_STATE_DO_RX: - _handler_sm_do_rx(); - break; - case TSCH_STATE_IS_RXING: - _handler_sm_is_rxing(); + DEBUG_GPIO_CLEAR(&pin1); + DEBUG_GPIO_SET(&pin2); + // update state + _set_next_state(TSCH_STATE_IS_RXING); + // receive packets + db_radio_rx(); // remember: this always starts with before the actual transmission begins, i.e, rx_offset < tx_offset always holds + _set_timer_and_compensate(TSCH_TIMER_INTRA_SLOT_CHANNEL, tsch_default_slot_timing.rx_max, start_ts, &_tsch_state_machine_handler); break; case TSCH_STATE_DO_TX: - _handler_sm_do_tx(); + DEBUG_GPIO_CLEAR(&pin1); + DEBUG_GPIO_SET(&pin2); + // update state + _set_next_state(TSCH_STATE_IS_TXING); + // send the packet + db_radio_tx(_tsch_vars.packet, _tsch_vars.packet_len); + _set_timer_and_compensate(TSCH_TIMER_INTRA_SLOT_CHANNEL, tsch_default_slot_timing.tx_max, start_ts, &_tsch_state_machine_handler); break; + // in case was receiving or sending, now just finish. timeslot will begin again because of the inter-slot timer + case TSCH_STATE_IS_RXING: case TSCH_STATE_IS_TXING: - _handler_sm_is_txing(); + DEBUG_GPIO_CLEAR(&pin2); + // just disable the radio and set the next state + db_radio_disable(); + _set_next_state(TSCH_STATE_BEGIN_SLOT); break; default: break; @@ -176,17 +207,12 @@ void _tsch_sm_handler(void) { } void _handler_sm_begin_slot(void) { -#ifdef DEBUG - db_gpio_toggle(&pin0); -#endif uint32_t start_ts = db_timer_hf_now(TSCH_TIMER_DEV); uint32_t timer_duration = 0; - // uint32_t start_sched_ts = db_timer_hf_now(TSCH_TIMER_DEV); - tsch_radio_event_t event = db_scheduler_tick(); - // printf("Scheduler tick took %d us\n", db_timer_hf_now(TSCH_TIMER_DEV) - start_sched_ts); - // printf("(_handler_sm_begin_slot) Event %c: %c, %d Slot duration: %d\n", event.slot_type, event.radio_action, event.frequency, tsch_default_slot_timing.total_duration); + tsch_radio_event_t event = db_scheduler_tick(_tsch_vars.asn++); + // printf(" Event %c: %c, %d Slot duration: %d\n", event.slot_type, event.radio_action, event.frequency, tsch_default_slot_timing.total_duration); switch (event.radio_action) { case TSCH_RADIO_ACTION_TX: @@ -202,10 +228,11 @@ void _handler_sm_begin_slot(void) { // TODO: how to get a packet? decide based on _tsch_vars.node_type and event.slot_type // could the event come with a packet? sometimes maybe? or would it be confusing? _tsch_vars.packet_len = sizeof(default_packet); - memcpy(_tsch_vars.packet, default_packet, _tsch_vars.packet_len); + memcpy(_tsch_vars.packet, default_packet, _tsch_vars.packet_len); // set timer duration to resume again after tx_offset timer_duration = tsch_default_slot_timing.tx_offset; + _set_timer_and_compensate(TSCH_TIMER_INTRA_SLOT_CHANNEL, timer_duration, start_ts, &_tsch_state_machine_handler); break; case TSCH_RADIO_ACTION_RX: // configure radio @@ -218,72 +245,50 @@ void _handler_sm_begin_slot(void) { // set timer duration to resume again after rx_offset timer_duration = tsch_default_slot_timing.rx_offset; + _set_timer_and_compensate(TSCH_TIMER_INTRA_SLOT_CHANNEL, timer_duration, start_ts, &_tsch_state_machine_handler); break; case TSCH_RADIO_ACTION_SLEEP: // just disable the radio and do nothing, then come back for the next slot db_radio_disable(); _set_next_state(TSCH_STATE_BEGIN_SLOT); // keep the same state - timer_duration = tsch_default_slot_timing.total_duration; + // timer_duration = tsch_default_slot_timing.total_duration; break; } - _set_timer_and_compensate(timer_duration, start_ts, &_tsch_sm_handler); -} - -// --- rx handlers --- -void _handler_sm_do_rx(void) { - uint32_t start_ts = db_timer_hf_now(TSCH_TIMER_DEV); - - // update state - _set_next_state(TSCH_STATE_IS_RXING); - - // receive packets - db_radio_rx(); // remember: this always starts with a guard time before the actual transmission begins - - _set_timer_and_compensate(tsch_default_slot_timing.rx_max, start_ts, &_tsch_sm_handler); + // _set_timer_and_compensate(TSCH_TIMER_INTRA_SLOT_CHANNEL, timer_duration, start_ts, &_tsch_state_machine_handler); } -void _handler_sm_is_rxing(void) { - // just disable the radio and do nothing, then come back for the next slot - uint32_t start_ts = db_timer_hf_now(TSCH_TIMER_DEV); - - // configure radio - db_radio_disable(); +// --------------------- timers --------------------- - // set state for after the timer expires - _set_next_state(TSCH_STATE_BEGIN_SLOT); - - _set_timer_and_compensate(tsch_default_slot_timing.end_guard, start_ts, &_tsch_sm_handler); +static inline void _set_next_state(tsch_state_t state) { + _tsch_vars.state = state; } -// --- tx handlers --- -void _handler_sm_do_tx(void) { - uint32_t start_ts = db_timer_hf_now(TSCH_TIMER_DEV); - - // update state - _set_next_state(TSCH_STATE_IS_TXING); - - // send the packet - db_radio_tx(_tsch_vars.packet, _tsch_vars.packet_len); - - _set_timer_and_compensate(tsch_default_slot_timing.tx_max, start_ts, &_tsch_sm_handler); +static inline void _set_timer_and_compensate(uint8_t channel, uint32_t duration, uint32_t start_ts, timer_hf_cb_t timer_callback) { + uint32_t elapsed_ts = db_timer_hf_now(TSCH_TIMER_DEV) - start_ts; + // printf("Setting timer for duration %d, compensating for elapsed %d gives: %d\n", duration, elapsed_ts, duration - elapsed_ts); + db_timer_hf_set_oneshot_us( + TSCH_TIMER_DEV, + channel, + duration - elapsed_ts, + timer_callback + ); } -void _handler_sm_is_txing(void) { - // just disable the radio and do nothing, then come back for the next slot - uint32_t start_ts = db_timer_hf_now(TSCH_TIMER_DEV); +//static inline void _set_timer(uint8_t channel, uint32_t duration, timer_hf_cb_t timer_callback) { +//// printf("Setting timer for duration %d\n", duration); +// db_timer_hf_set_oneshot_us( +// TSCH_TIMER_DEV, +// channel, +// duration, +// timer_callback +// ); +//} - // configure radio - db_radio_disable(); - - // set state for after the timer expires - _set_next_state(TSCH_STATE_BEGIN_SLOT); - - _set_timer_and_compensate(tsch_default_slot_timing.end_guard, start_ts, &_tsch_sm_handler); -} +// --------------------- others --------------------- static void _tsch_callback(uint8_t *packet, uint8_t length) { - // printf("Received packet of length %d\n", length); + printf("Received packet of length %d\n", length); // Check if the packet is big enough to have a header if (length < sizeof(protocol_header_t)) { @@ -297,18 +302,3 @@ static void _tsch_callback(uint8_t *packet, uint8_t length) { _tsch_vars.application_callback(packet, length); } } - -static inline void _set_next_state(tsch_state_t state) { - _tsch_vars.state = state; -} - -static inline void _set_timer_and_compensate(uint32_t duration, uint32_t start_ts, timer_hf_cb_t timer_callback) { - uint32_t elapsed_ts = db_timer_hf_now(TSCH_TIMER_DEV) - start_ts; - // printf("Setting timer for duration %d, compensating for elapsed %d gives: %d\n", duration, elapsed_ts, duration - elapsed_ts); - db_timer_hf_set_oneshot_us( - TSCH_TIMER_DEV, - TSCH_TIMER_SLOT_CHANNEL, - duration - elapsed_ts, - timer_callback - ); -} diff --git a/projects/01drv_scheduler/01drv_scheduler.c b/projects/01drv_scheduler/01drv_scheduler.c index 6716d9d57..66f91d6d5 100644 --- a/projects/01drv_scheduler/01drv_scheduler.c +++ b/projects/01drv_scheduler/01drv_scheduler.c @@ -19,6 +19,8 @@ #include "protocol.h" #include "device.h" +#define SLOT_DURATION 1000 * 1000 // 1 s + // make some schedules available for testing #include "test_schedules.c" extern schedule_t schedule_minuscule; @@ -37,13 +39,14 @@ int main(void) { // loop n_slotframes*n_cells times and make the scheduler tick // also, try to assign and deassign uplink cell at specific slotframes size_t n_slotframes = 4; + uint64_t asn = 0; for (size_t j = 0; j < n_slotframes; j++) { for (size_t i = 0; i < schedule.n_cells; i++) { - tsch_radio_event_t event = db_scheduler_tick(); - printf("Event %c: %c, %d, %d\n", event.slot_type, event.radio_action, event.frequency, event.duration_us); + tsch_radio_event_t event = db_scheduler_tick(asn++); + printf("Event %c: %c, %d, %d\n", event.slot_type, event.radio_action, event.frequency); // sleep for the duration of the slot - db_timer_hf_delay_us(TSCH_TIMER_DEV, event.duration_us); + db_timer_hf_delay_us(TSCH_TIMER_DEV, SLOT_DURATION); } puts("."); if (j == 0 && !db_scheduler_assign_next_available_uplink_cell(db_device_id())) { // try to assign at the end of first slotframe diff --git a/projects/01drv_tsch/01drv_tsch.c b/projects/01drv_tsch/01drv_tsch.c index 04e22ab9f..840c2e06b 100644 --- a/projects/01drv_tsch/01drv_tsch.c +++ b/projects/01drv_tsch/01drv_tsch.c @@ -53,14 +53,14 @@ static void radio_callback(uint8_t *packet, uint8_t length); int main(void) { // initialize schedule - schedule_t schedule = schedule_test; + schedule_t schedule = schedule_minuscule; node_type_t node_type = NODE_TYPE_DOTBOT; db_scheduler_init(node_type, &schedule); printf("Device of type %c and id %llx is using schedule %d\n\n", node_type, db_device_id(), schedule.id); // initialize the TSCH driver //tsch_default_slot_timing.end_guard = 1000 * 1000; // add an extra second of delay. - db_tsch_init(radio_callback); + db_tsch_init(node_type, radio_callback); printf("Slot total duration: %d us\n", tsch_default_slot_timing.total_duration); while (1) { From 1e5da6928f9f2bc2013e86ca78b6161c2740f759 Mon Sep 17 00:00:00 2001 From: Geovane Fedrecheski Date: Wed, 8 Jan 2025 18:36:02 +0100 Subject: [PATCH 12/17] tsch: add beacon-only schedules --- drv/scheduler.h | 2 +- drv/scheduler/all_schedules.c | 29 +++++++++++++++++++++- drv/scheduler/scheduler.c | 4 +++ projects/01drv_scheduler/01drv_scheduler.c | 6 ++--- projects/01drv_tsch/01drv_tsch.c | 4 +-- 5 files changed, 38 insertions(+), 7 deletions(-) diff --git a/drv/scheduler.h b/drv/scheduler.h index 396c0391b..0aa8fb1a3 100644 --- a/drv/scheduler.h +++ b/drv/scheduler.h @@ -33,7 +33,7 @@ #define TSCH_N_CELLS_MAX 101 -// #define TSCH_LISTEN_DURING_UNSCHEDULED_UPLINK 1 +#define TSCH_LISTEN_DURING_UNSCHEDULED_UPLINK 1 //=========================== variables ======================================== diff --git a/drv/scheduler/all_schedules.c b/drv/scheduler/all_schedules.c index 704edbe54..78ad54a1f 100644 --- a/drv/scheduler/all_schedules.c +++ b/drv/scheduler/all_schedules.c @@ -10,7 +10,34 @@ */ #include "scheduler.h" -#define TSCH_N_SCHEDULES 1 + 2 // account for the schedule that can be passed by the application during initialization +#define TSCH_N_SCHEDULES 1 + 4 // account for the schedule that can be passed by the application during initialization + +/* Schedule only for beacons. Used when scanning the network. */ +schedule_t schedule_only_beacons = { + .id = 0xBE, + .max_nodes = 0, + .backoff_n_min = 5, + .backoff_n_max = 9, + .n_cells = 3, + .cells = { + {'B', 0, NULL}, + {'B', 1, NULL}, + {'B', 2, NULL}, + } +}; + +/* Schedule only for beacons, used for network scanning, specifically when the TSCH_LISTEN_DURING_UNSCHEDULED_UPLINK optimization is enabled. */ +schedule_t schedule_only_beacons_optimized_scan = { + .id = 0xBF, + .max_nodes = 0, + .backoff_n_min = 5, + .backoff_n_max = 9, + .n_cells = 1, + .cells = { + // the channel offset doesn't matter here + {'U', 0, NULL}, + } +}; /* Schedule with 11 slots, supporting up to 5 nodes */ schedule_t schedule_minuscule = { diff --git a/drv/scheduler/scheduler.c b/drv/scheduler/scheduler.c index f944e1cb8..909be41a3 100644 --- a/drv/scheduler/scheduler.c +++ b/drv/scheduler/scheduler.c @@ -73,8 +73,12 @@ void db_scheduler_init(node_type_t node_type, schedule_t *application_schedule) if (_schedule_vars.available_schedules_len == TSCH_N_SCHEDULES) return; // FIXME: this is just to simplify debugging (allows calling init multiple times) + _schedule_vars.available_schedules[_schedule_vars.available_schedules_len++] = schedule_only_beacons; + _schedule_vars.available_schedules[_schedule_vars.available_schedules_len++] = schedule_only_beacons_optimized_scan; + _schedule_vars.available_schedules[_schedule_vars.available_schedules_len++] = schedule_minuscule; _schedule_vars.available_schedules[_schedule_vars.available_schedules_len++] = schedule_tiny; + if (application_schedule != NULL) { _schedule_vars.available_schedules[_schedule_vars.available_schedules_len++] = *application_schedule; _schedule_vars.active_schedule_ptr = application_schedule; diff --git a/projects/01drv_scheduler/01drv_scheduler.c b/projects/01drv_scheduler/01drv_scheduler.c index 66f91d6d5..06c058073 100644 --- a/projects/01drv_scheduler/01drv_scheduler.c +++ b/projects/01drv_scheduler/01drv_scheduler.c @@ -23,14 +23,14 @@ // make some schedules available for testing #include "test_schedules.c" -extern schedule_t schedule_minuscule; +extern schedule_t schedule_minuscule, schedule_only_beacons_optimized_scan; int main(void) { // initialize high frequency timer db_timer_hf_init(TSCH_TIMER_DEV); // initialize schedule - schedule_t schedule = schedule_minuscule; + schedule_t schedule = schedule_only_beacons_optimized_scan; node_type_t node_type = NODE_TYPE_DOTBOT; db_scheduler_init(node_type, &schedule); @@ -43,7 +43,7 @@ int main(void) { for (size_t j = 0; j < n_slotframes; j++) { for (size_t i = 0; i < schedule.n_cells; i++) { tsch_radio_event_t event = db_scheduler_tick(asn++); - printf("Event %c: %c, %d, %d\n", event.slot_type, event.radio_action, event.frequency); + printf(">> Event %c: %c, %d\n", event.slot_type, event.radio_action, event.frequency); // sleep for the duration of the slot db_timer_hf_delay_us(TSCH_TIMER_DEV, SLOT_DURATION); diff --git a/projects/01drv_tsch/01drv_tsch.c b/projects/01drv_tsch/01drv_tsch.c index 840c2e06b..06e3dfa5b 100644 --- a/projects/01drv_tsch/01drv_tsch.c +++ b/projects/01drv_tsch/01drv_tsch.c @@ -47,13 +47,13 @@ schedule_t schedule_test = { } }; -extern schedule_t schedule_minuscule, schedule_small; +extern schedule_t schedule_minuscule, schedule_small, schedule_only_beacons_optimized_scan; static void radio_callback(uint8_t *packet, uint8_t length); int main(void) { // initialize schedule - schedule_t schedule = schedule_minuscule; + schedule_t schedule = schedule_only_beacons_optimized_scan; node_type_t node_type = NODE_TYPE_DOTBOT; db_scheduler_init(node_type, &schedule); printf("Device of type %c and id %llx is using schedule %d\n\n", node_type, db_device_id(), schedule.id); From f89f7549abdb8e7174a470358c1f70eaa287e68e Mon Sep 17 00:00:00 2001 From: Geovane Fedrecheski Date: Wed, 8 Jan 2025 19:04:33 +0100 Subject: [PATCH 13/17] tsch-WIP: send beacons --- drv/tsch/tsch.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/drv/tsch/tsch.c b/drv/tsch/tsch.c index 40844c071..2b96c391e 100644 --- a/drv/tsch/tsch.c +++ b/drv/tsch/tsch.c @@ -100,6 +100,8 @@ uint8_t default_packet[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 }; +tsch_beacon_packet_header_t beacon = { 0 }; + //========================== prototypes ======================================== //-------------------------- state machine handlers ---------------------------- @@ -148,6 +150,10 @@ void db_tsch_init(node_type_t node_type, tsch_cb_t application_callback) { _tsch_vars.asn = 0; + // configure beacon + beacon.version = 1; + beacon.src = db_device_id(); + // NOTE: assume the scheduler has already been initialized by the application // set slot total duration @@ -227,8 +233,14 @@ void _handler_sm_begin_slot(void) { // get the packet to tx and save in _tsch_vars // TODO: how to get a packet? decide based on _tsch_vars.node_type and event.slot_type // could the event come with a packet? sometimes maybe? or would it be confusing? - _tsch_vars.packet_len = sizeof(default_packet); - memcpy(_tsch_vars.packet, default_packet, _tsch_vars.packet_len); + + if (event.slot_type == TSCH_PACKET_TYPE_BEACON) { + _tsch_vars.packet_len = sizeof(beacon); + memcpy(_tsch_vars.packet, &beacon, _tsch_vars.packet_len); + } else { + _tsch_vars.packet_len = sizeof(default_packet); + memcpy(_tsch_vars.packet, default_packet, _tsch_vars.packet_len); + } // set timer duration to resume again after tx_offset timer_duration = tsch_default_slot_timing.tx_offset; From 22143ff6fd7c55f7e222a2cfea14082be845c7d9 Mon Sep 17 00:00:00 2001 From: Geovane Fedrecheski Date: Thu, 9 Jan 2025 11:30:47 +0100 Subject: [PATCH 14/17] tsch: basic beacon tx/rx works --- drv/scheduler.h | 9 +-------- drv/tsch.h | 9 ++++++++- drv/tsch/tsch.c | 28 ++++++++++++++++++++-------- projects/01drv_tsch/01drv_tsch.c | 22 ++++++++++++++-------- 4 files changed, 43 insertions(+), 25 deletions(-) diff --git a/drv/scheduler.h b/drv/scheduler.h index 0aa8fb1a3..acc81400a 100644 --- a/drv/scheduler.h +++ b/drv/scheduler.h @@ -33,17 +33,10 @@ #define TSCH_N_CELLS_MAX 101 -#define TSCH_LISTEN_DURING_UNSCHEDULED_UPLINK 1 +// #define TSCH_LISTEN_DURING_UNSCHEDULED_UPLINK 1 //=========================== variables ======================================== -typedef enum { - SLOT_TYPE_BEACON = 'B', - SLOT_TYPE_SHARED_UPLINK = 'S', - SLOT_TYPE_DOWNLINK = 'D', - SLOT_TYPE_UPLINK = 'U', -} slot_type_t; // FIXME: slot_type or cell_type? - typedef struct { slot_type_t type; uint8_t channel_offset; diff --git a/drv/tsch.h b/drv/tsch.h index cd1646bc5..61d68ef49 100644 --- a/drv/tsch.h +++ b/drv/tsch.h @@ -49,6 +49,13 @@ typedef enum { TSCH_RADIO_ACTION_TX = 'T', } tsch_radio_action_t; +typedef enum { + SLOT_TYPE_BEACON = 'B', + SLOT_TYPE_SHARED_UPLINK = 'S', + SLOT_TYPE_DOWNLINK = 'D', + SLOT_TYPE_UPLINK = 'U', +} slot_type_t; // FIXME: slot_type or cell_type? + /* Timing of intra-slot sections */ typedef struct { uint32_t rx_offset; ///< Offset for the receiver to start receiving. @@ -92,7 +99,7 @@ typedef struct { typedef struct { tsch_radio_action_t radio_action; uint8_t frequency; - char slot_type; // FIXME: only for debugging, remove before merge + slot_type_t slot_type; } tsch_radio_event_t; typedef void (*tsch_cb_t)(uint8_t *packet, uint8_t length); ///< Function pointer to the callback function called on packet receive diff --git a/drv/tsch/tsch.c b/drv/tsch/tsch.c index 2b96c391e..4393531a4 100644 --- a/drv/tsch/tsch.c +++ b/drv/tsch/tsch.c @@ -152,6 +152,7 @@ void db_tsch_init(node_type_t node_type, tsch_cb_t application_callback) { // configure beacon beacon.version = 1; + beacon.type = TSCH_PACKET_TYPE_BEACON; beacon.src = db_device_id(); // NOTE: assume the scheduler has already been initialized by the application @@ -174,8 +175,8 @@ void _tsch_state_machine_handler(void) { switch (_tsch_vars.state) { case TSCH_STATE_BEGIN_SLOT: - DEBUG_GPIO_TOGGLE(&pin0); - DEBUG_GPIO_SET(&pin1); + DEBUG_GPIO_CLEAR(&pin0); DEBUG_GPIO_SET(&pin0); // slot-wide debug pin + DEBUG_GPIO_SET(&pin1); // intra-slot debug pin // set the timer for the next slot TOTAL DURATION // _set_timer(TSCH_TIMER_INTER_SLOT_CHANNEL, tsch_default_slot_timing.total_duration, &_tsch_state_machine_handler); _set_timer_and_compensate(TSCH_TIMER_INTER_SLOT_CHANNEL, tsch_default_slot_timing.total_duration, start_ts, &_tsch_state_machine_handler); @@ -183,7 +184,7 @@ void _tsch_state_machine_handler(void) { break; case TSCH_STATE_DO_RX: DEBUG_GPIO_CLEAR(&pin1); - DEBUG_GPIO_SET(&pin2); + DEBUG_GPIO_SET(&pin1); // update state _set_next_state(TSCH_STATE_IS_RXING); // receive packets @@ -192,17 +193,25 @@ void _tsch_state_machine_handler(void) { break; case TSCH_STATE_DO_TX: DEBUG_GPIO_CLEAR(&pin1); - DEBUG_GPIO_SET(&pin2); + DEBUG_GPIO_SET(&pin1); // update state _set_next_state(TSCH_STATE_IS_TXING); // send the packet + // printf("Sending packet of length %d\n", _tsch_vars.packet_len); + // for (uint8_t i = 0; i < _tsch_vars.packet_len; i++) { + // printf("%02x ", _tsch_vars.packet[i]); + // } + // puts(""); + if (_tsch_vars.node_type == NODE_TYPE_GATEWAY && _tsch_vars.event.slot_type == SLOT_TYPE_BEACON) { + DEBUG_GPIO_CLEAR(&pin2); // Gateway sending beacon NOW + } db_radio_tx(_tsch_vars.packet, _tsch_vars.packet_len); _set_timer_and_compensate(TSCH_TIMER_INTRA_SLOT_CHANNEL, tsch_default_slot_timing.tx_max, start_ts, &_tsch_state_machine_handler); break; // in case was receiving or sending, now just finish. timeslot will begin again because of the inter-slot timer case TSCH_STATE_IS_RXING: case TSCH_STATE_IS_TXING: - DEBUG_GPIO_CLEAR(&pin2); + DEBUG_GPIO_CLEAR(&pin1); // just disable the radio and set the next state db_radio_disable(); _set_next_state(TSCH_STATE_BEGIN_SLOT); @@ -234,9 +243,10 @@ void _handler_sm_begin_slot(void) { // TODO: how to get a packet? decide based on _tsch_vars.node_type and event.slot_type // could the event come with a packet? sometimes maybe? or would it be confusing? - if (event.slot_type == TSCH_PACKET_TYPE_BEACON) { + if (event.slot_type == SLOT_TYPE_BEACON) { _tsch_vars.packet_len = sizeof(beacon); memcpy(_tsch_vars.packet, &beacon, _tsch_vars.packet_len); + DEBUG_GPIO_SET(&pin2); // Gateway prepared a beacon to send } else { _tsch_vars.packet_len = sizeof(default_packet); memcpy(_tsch_vars.packet, default_packet, _tsch_vars.packet_len); @@ -300,7 +310,7 @@ static inline void _set_timer_and_compensate(uint8_t channel, uint32_t duration, // --------------------- others --------------------- static void _tsch_callback(uint8_t *packet, uint8_t length) { - printf("Received packet of length %d\n", length); + // printf("TSCH: Received packet of length %d\n", length); // Check if the packet is big enough to have a header if (length < sizeof(protocol_header_t)) { @@ -308,8 +318,10 @@ static void _tsch_callback(uint8_t *packet, uint8_t length) { } // Check if it is a beacon - if (packet[1] == TSCH_PACKET_TYPE_BEACON) { + if (packet[1] == TSCH_PACKET_TYPE_BEACON && _tsch_vars.node_type == NODE_TYPE_DOTBOT) { // _tsch_handle_beacon(packet, length); + DEBUG_GPIO_SET(&pin2); // DotBot received a beacon + DEBUG_GPIO_CLEAR(&pin2); } else if (_tsch_vars.application_callback) { _tsch_vars.application_callback(packet, length); } diff --git a/projects/01drv_tsch/01drv_tsch.c b/projects/01drv_tsch/01drv_tsch.c index 06e3dfa5b..0e3db782a 100644 --- a/projects/01drv_tsch/01drv_tsch.c +++ b/projects/01drv_tsch/01drv_tsch.c @@ -47,16 +47,20 @@ schedule_t schedule_test = { } }; -extern schedule_t schedule_minuscule, schedule_small, schedule_only_beacons_optimized_scan; +extern schedule_t schedule_minuscule, schedule_small, schedule_only_beacons, schedule_only_beacons_optimized_scan; static void radio_callback(uint8_t *packet, uint8_t length); int main(void) { // initialize schedule - schedule_t schedule = schedule_only_beacons_optimized_scan; + + //schedule_t schedule = schedule_only_beacons; + //node_type_t node_type = NODE_TYPE_GATEWAY; + schedule_t schedule = schedule_small; node_type_t node_type = NODE_TYPE_DOTBOT; + db_scheduler_init(node_type, &schedule); - printf("Device of type %c and id %llx is using schedule %d\n\n", node_type, db_device_id(), schedule.id); + printf("\n==== Device of type %c and id %llx is using schedule %d ====\n\n", node_type, db_device_id(), schedule.id); // initialize the TSCH driver //tsch_default_slot_timing.end_guard = 1000 * 1000; // add an extra second of delay. @@ -69,9 +73,11 @@ int main(void) { } static void radio_callback(uint8_t *packet, uint8_t length) { - printf("Received packet of length %d\n", length); - for (uint8_t i = 0; i < length; i++) { - printf("%02x ", packet[i]); - } - puts(""); + (void) packet; + (void) length; + //printf("Received packet of length %d\n", length); + //for (uint8_t i = 0; i < length; i++) { + // printf("%02x ", packet[i]); + //} + //puts(""); } From 4032d18954c84124c66f98a86ed046f7b2a65de4 Mon Sep 17 00:00:00 2001 From: Geovane Fedrecheski Date: Thu, 9 Jan 2025 17:18:13 +0100 Subject: [PATCH 15/17] tsch: experimenting with multiple beacons per slot --- drv/scheduler.h | 2 +- drv/tsch/tsch.c | 51 ++++++++++++++++++++++++++------ projects/01drv_tsch/01drv_tsch.c | 8 ++--- 3 files changed, 47 insertions(+), 14 deletions(-) diff --git a/drv/scheduler.h b/drv/scheduler.h index acc81400a..9f9840598 100644 --- a/drv/scheduler.h +++ b/drv/scheduler.h @@ -33,7 +33,7 @@ #define TSCH_N_CELLS_MAX 101 -// #define TSCH_LISTEN_DURING_UNSCHEDULED_UPLINK 1 +#define TSCH_LISTEN_DURING_UNSCHEDULED_UPLINK 1 //=========================== variables ======================================== diff --git a/drv/tsch/tsch.c b/drv/tsch/tsch.c index 4393531a4..3d2a368a7 100644 --- a/drv/tsch/tsch.c +++ b/drv/tsch/tsch.c @@ -46,6 +46,8 @@ gpio_t pin3 = { .port = 1, .pin = 5 }; #define DEBUG_GPIO_SET(pin) ((void)0)) #endif +#define TSCH_TX_TIME_ONE_BEACON ((sizeof(beacon) * BLE_2M_US_PER_BYTE) + 150) + //=========================== defines ========================================== //=========================== variables ======================================== @@ -90,6 +92,10 @@ typedef struct { uint8_t packet[DB_BLE_PAYLOAD_MAX_LENGTH]; ///< Buffer to store the packet to transmit size_t packet_len; ///< Length of the packet to transmit + uint8_t max_beacons_per_slot; ///< Maximum number of beacons to send in a single slot + uint8_t beacons_sent_this_slot; ///< Tracker to allow sending several beacons in the same slot + uint8_t tx_time_one_beacon; ///< Tracker to allow sending several beacons in the same slot + tsch_cb_t application_callback; ///< Function pointer, stores the application callback } tsch_vars_t; @@ -150,10 +156,13 @@ void db_tsch_init(node_type_t node_type, tsch_cb_t application_callback) { _tsch_vars.asn = 0; - // configure beacon + // beacon configurations beacon.version = 1; beacon.type = TSCH_PACKET_TYPE_BEACON; beacon.src = db_device_id(); + _tsch_vars.max_beacons_per_slot = tsch_default_slot_timing.tx_max / ((sizeof(beacon) * BLE_2M_US_PER_BYTE) + 180); // 180 us margin + _tsch_vars.beacons_sent_this_slot = 0; + // _tsch_vars.tx_time_one_beacon = (sizeof(beacon) * BLE_2M_US_PER_BYTE) + 100; // 100 us margin // NOTE: assume the scheduler has already been initialized by the application @@ -162,7 +171,7 @@ void db_tsch_init(node_type_t node_type, tsch_cb_t application_callback) { // initialize and start the state machine _set_next_state(TSCH_STATE_BEGIN_SLOT); - uint32_t time_padding = 10; // account for function call and interrupt latency + uint32_t time_padding = 50; // account for function call and interrupt latency db_timer_hf_set_oneshot_us(TSCH_TIMER_DEV, TSCH_TIMER_INTER_SLOT_CHANNEL, time_padding, _tsch_state_machine_handler); // trigger the state machine } @@ -180,6 +189,11 @@ void _tsch_state_machine_handler(void) { // set the timer for the next slot TOTAL DURATION // _set_timer(TSCH_TIMER_INTER_SLOT_CHANNEL, tsch_default_slot_timing.total_duration, &_tsch_state_machine_handler); _set_timer_and_compensate(TSCH_TIMER_INTER_SLOT_CHANNEL, tsch_default_slot_timing.total_duration, start_ts, &_tsch_state_machine_handler); + + // common logic, which doesn't depend on schedule + _tsch_vars.beacons_sent_this_slot = 0; + + // logic of beginning the slot _handler_sm_begin_slot(); break; case TSCH_STATE_DO_RX: @@ -194,19 +208,38 @@ void _tsch_state_machine_handler(void) { case TSCH_STATE_DO_TX: DEBUG_GPIO_CLEAR(&pin1); DEBUG_GPIO_SET(&pin1); - // update state - _set_next_state(TSCH_STATE_IS_TXING); // send the packet // printf("Sending packet of length %d\n", _tsch_vars.packet_len); // for (uint8_t i = 0; i < _tsch_vars.packet_len; i++) { // printf("%02x ", _tsch_vars.packet[i]); // } // puts(""); - if (_tsch_vars.node_type == NODE_TYPE_GATEWAY && _tsch_vars.event.slot_type == SLOT_TYPE_BEACON) { - DEBUG_GPIO_CLEAR(&pin2); // Gateway sending beacon NOW + + // FIXME: this code is ugly, there must be a better way. + if (_tsch_vars.node_type == NODE_TYPE_GATEWAY) { + // we want to send several beacons in the same slot, to maximize chance or reception. + // this is important because a DotBot will always be un-synced with relation to a Beacon from a Gateway it is not associated with. + if (_tsch_vars.event.slot_type == SLOT_TYPE_BEACON) { + if (_tsch_vars.beacons_sent_this_slot++ < _tsch_vars.max_beacons_per_slot) { + // stay in the same state, so that we can send still one more beacon + _set_next_state(TSCH_STATE_DO_TX); + DEBUG_GPIO_SET(&pin2); DEBUG_GPIO_CLEAR(&pin2); // Gateway sending beacon NOW + _set_timer_and_compensate(TSCH_TIMER_INTRA_SLOT_CHANNEL, TSCH_TX_TIME_ONE_BEACON, start_ts, &_tsch_state_machine_handler); + db_radio_tx(_tsch_vars.packet, _tsch_vars.packet_len); + break; + } else { + // we have sent all beacons for this slot, so we can just finish the TX phase + _set_next_state(TSCH_STATE_IS_TXING); + // _set_timer_and_compensate(TSCH_TIMER_INTRA_SLOT_CHANNEL, 100, start_ts, &_tsch_state_machine_handler); + _tsch_state_machine_handler(); + break; + } + } } - db_radio_tx(_tsch_vars.packet, _tsch_vars.packet_len); + + _set_next_state(TSCH_STATE_IS_TXING); _set_timer_and_compensate(TSCH_TIMER_INTRA_SLOT_CHANNEL, tsch_default_slot_timing.tx_max, start_ts, &_tsch_state_machine_handler); + db_radio_tx(_tsch_vars.packet, _tsch_vars.packet_len); break; // in case was receiving or sending, now just finish. timeslot will begin again because of the inter-slot timer case TSCH_STATE_IS_RXING: @@ -243,10 +276,9 @@ void _handler_sm_begin_slot(void) { // TODO: how to get a packet? decide based on _tsch_vars.node_type and event.slot_type // could the event come with a packet? sometimes maybe? or would it be confusing? - if (event.slot_type == SLOT_TYPE_BEACON) { + if (_tsch_vars.node_type == NODE_TYPE_GATEWAY && event.slot_type == SLOT_TYPE_BEACON) { _tsch_vars.packet_len = sizeof(beacon); memcpy(_tsch_vars.packet, &beacon, _tsch_vars.packet_len); - DEBUG_GPIO_SET(&pin2); // Gateway prepared a beacon to send } else { _tsch_vars.packet_len = sizeof(default_packet); memcpy(_tsch_vars.packet, default_packet, _tsch_vars.packet_len); @@ -273,6 +305,7 @@ void _handler_sm_begin_slot(void) { // just disable the radio and do nothing, then come back for the next slot db_radio_disable(); _set_next_state(TSCH_STATE_BEGIN_SLOT); // keep the same state + DEBUG_GPIO_CLEAR(&pin1); // timer_duration = tsch_default_slot_timing.total_duration; break; } diff --git a/projects/01drv_tsch/01drv_tsch.c b/projects/01drv_tsch/01drv_tsch.c index 0e3db782a..3ccfebf55 100644 --- a/projects/01drv_tsch/01drv_tsch.c +++ b/projects/01drv_tsch/01drv_tsch.c @@ -54,10 +54,10 @@ static void radio_callback(uint8_t *packet, uint8_t length); int main(void) { // initialize schedule - //schedule_t schedule = schedule_only_beacons; - //node_type_t node_type = NODE_TYPE_GATEWAY; - schedule_t schedule = schedule_small; - node_type_t node_type = NODE_TYPE_DOTBOT; + schedule_t schedule = schedule_only_beacons; + node_type_t node_type = NODE_TYPE_GATEWAY; + //schedule_t schedule = schedule_small; + //node_type_t node_type = NODE_TYPE_DOTBOT; db_scheduler_init(node_type, &schedule); printf("\n==== Device of type %c and id %llx is using schedule %d ====\n\n", node_type, db_device_id(), schedule.id); From 19fca906d7ce1e795cc7d4bf829b4916f8d874c4 Mon Sep 17 00:00:00 2001 From: Geovane Fedrecheski Date: Thu, 9 Jan 2025 18:08:30 +0100 Subject: [PATCH 16/17] tsch: cache packet for radio in advance, simplify --- bsp/nrf/radio_default.c | 26 +++ bsp/radio.h | 3 + drv/scheduler.h | 2 +- drv/scheduler/all_schedules.c | 263 ++++++++++++++++++++++++++++++- drv/tsch/tsch.c | 46 ++---- projects/01drv_tsch/01drv_tsch.c | 10 +- 6 files changed, 313 insertions(+), 37 deletions(-) diff --git a/bsp/nrf/radio_default.c b/bsp/nrf/radio_default.c index b21b4da49..28c99db6c 100644 --- a/bsp/nrf/radio_default.c +++ b/bsp/nrf/radio_default.c @@ -281,6 +281,32 @@ int8_t db_radio_rssi(void) { return (uint8_t)NRF_RADIO->RSSISAMPLE * -1; } + +void db_radio_tx_prepare(const uint8_t *tx_buffer, uint8_t length) { + radio_vars.pdu.length = length; + memcpy(radio_vars.pdu.payload, tx_buffer, length); +} + +void db_radio_tx_dispatch(void) { + NRF_RADIO->SHORTS = RADIO_SHORTS_COMMON | (RADIO_SHORTS_DISABLED_RXEN_Enabled << RADIO_SHORTS_DISABLED_RXEN_Pos); + if (radio_vars.state == RADIO_STATE_IDLE) { + + // Enable the Radio to send the packet + NRF_RADIO->EVENTS_DISABLED = 0; // We must use EVENT_DISABLED, if we use EVENT_END. the interrupts will be enabled in the time between the END event and the Disable event, triggering an undesired interrupt + NRF_RADIO->TASKS_TXEN = RADIO_TASKS_TXEN_TASKS_TXEN_Trigger << RADIO_TASKS_TXEN_TASKS_TXEN_Pos; + // Wait for transmission to end and the radio to be disabled + while (NRF_RADIO->EVENTS_DISABLED == 0) {} + + // We re-enable interrupts AFTER the packet is sent, to avoid triggering an EVENT_ADDRESS and EVENT_DISABLED interrupt with the outgoing packet + // We also clear both flags to avoid insta-triggering an interrupt as soon as we assert INTENSET + NRF_RADIO->EVENTS_ADDRESS = 0; + NRF_RADIO->EVENTS_DISABLED = 0; + _radio_enable(); + } + radio_vars.state = RADIO_STATE_RX; +} + + //=========================== private ========================================== static void _radio_enable(void) { diff --git a/bsp/radio.h b/bsp/radio.h index 64babb712..741d3894e 100644 --- a/bsp/radio.h +++ b/bsp/radio.h @@ -121,4 +121,7 @@ int8_t db_radio_rssi(void); */ void db_radio_disable(void); +void db_radio_tx_prepare(const uint8_t *tx_buffer, uint8_t length); +void db_radio_tx_dispatch(void); + #endif diff --git a/drv/scheduler.h b/drv/scheduler.h index 9f9840598..7cc7060a6 100644 --- a/drv/scheduler.h +++ b/drv/scheduler.h @@ -31,7 +31,7 @@ #define TSCH_N_BLE_REGULAR_FREQUENCIES 37 #define TSCH_N_BLE_ADVERTISING_FREQUENCIES 3 -#define TSCH_N_CELLS_MAX 101 +#define TSCH_N_CELLS_MAX 137 #define TSCH_LISTEN_DURING_UNSCHEDULED_UPLINK 1 diff --git a/drv/scheduler/all_schedules.c b/drv/scheduler/all_schedules.c index 78ad54a1f..8b4a325e4 100644 --- a/drv/scheduler/all_schedules.c +++ b/drv/scheduler/all_schedules.c @@ -10,7 +10,7 @@ */ #include "scheduler.h" -#define TSCH_N_SCHEDULES 1 + 4 // account for the schedule that can be passed by the application during initialization +#define TSCH_N_SCHEDULES 1 + 7 // account for the schedule that can be passed by the application during initialization /* Schedule only for beacons. Used when scanning the network. */ schedule_t schedule_only_beacons = { @@ -144,3 +144,264 @@ schedule_t schedule_small = { {'U', 26, NULL}, } }; + +/* Schedule with 101 slots, supporting up to 74 nodes */ +schedule_t schedule_big = { + .id = 2, + .max_nodes = 74, + .backoff_n_min = 5, + .backoff_n_max = 9, + .n_cells = 101, + .cells = { + {'B', 0, NULL}, + {'B', 1, NULL}, + {'B', 2, NULL}, + {'S', 23, NULL}, + {'D', 74, NULL}, + {'U', 78, NULL}, + {'U', 97, NULL}, + {'U', 63, NULL}, + {'U', 32, NULL}, + {'D', 59, NULL}, + {'U', 21, NULL}, + {'U', 9, NULL}, + {'U', 48, NULL}, + {'U', 53, NULL}, + {'U', 79, NULL}, + {'S', 92, NULL}, + {'D', 71, NULL}, + {'U', 26, NULL}, + {'U', 81, NULL}, + {'U', 27, NULL}, + {'U', 89, NULL}, + {'D', 1, NULL}, + {'U', 56, NULL}, + {'U', 6, NULL}, + {'U', 46, NULL}, + {'U', 34, NULL}, + {'U', 19, NULL}, + {'S', 60, NULL}, + {'D', 15, NULL}, + {'U', 58, NULL}, + {'U', 72, NULL}, + {'U', 42, NULL}, + {'U', 41, NULL}, + {'D', 50, NULL}, + {'U', 73, NULL}, + {'U', 4, NULL}, + {'U', 55, NULL}, + {'U', 16, NULL}, + {'U', 90, NULL}, + {'S', 69, NULL}, + {'D', 7, NULL}, + {'U', 95, NULL}, + {'U', 24, NULL}, + {'U', 84, NULL}, + {'U', 33, NULL}, + {'D', 76, NULL}, + {'U', 94, NULL}, + {'U', 62, NULL}, + {'U', 93, NULL}, + {'U', 45, NULL}, + {'U', 83, NULL}, + {'S', 49, NULL}, + {'D', 13, NULL}, + {'U', 65, NULL}, + {'U', 39, NULL}, + {'U', 12, NULL}, + {'U', 67, NULL}, + {'D', 5, NULL}, + {'U', 36, NULL}, + {'U', 44, NULL}, + {'U', 10, NULL}, + {'U', 66, NULL}, + {'U', 88, NULL}, + {'S', 61, NULL}, + {'D', 47, NULL}, + {'U', 35, NULL}, + {'U', 87, NULL}, + {'U', 70, NULL}, + {'U', 2, NULL}, + {'D', 82, NULL}, + {'U', 17, NULL}, + {'U', 28, NULL}, + {'U', 14, NULL}, + {'U', 8, NULL}, + {'U', 22, NULL}, + {'S', 51, NULL}, + {'D', 91, NULL}, + {'U', 85, NULL}, + {'U', 68, NULL}, + {'U', 86, NULL}, + {'U', 80, NULL}, + {'D', 75, NULL}, + {'U', 25, NULL}, + {'U', 54, NULL}, + {'U', 57, NULL}, + {'U', 3, NULL}, + {'U', 38, NULL}, + {'S', 37, NULL}, + {'D', 20, NULL}, + {'U', 18, NULL}, + {'U', 64, NULL}, + {'U', 30, NULL}, + {'U', 31, NULL}, + {'D', 96, NULL}, + {'U', 11, NULL}, + {'U', 77, NULL}, + {'U', 29, NULL}, + {'U', 0, NULL}, + {'U', 43, NULL}, + {'U', 40, NULL}, + {'U', 52, NULL} + } +}; + + +/* Schedule with 137 slots, supporting up to 101 nodes */ +schedule_t schedule_huge = { + .id = 1, + .max_nodes = 101, + .backoff_n_min = 5, + .backoff_n_max = 9, + .n_cells = 137, + .cells = { + {'B', 0, NULL}, + {'B', 1, NULL}, + {'B', 2, NULL}, + {'S', 9, NULL}, + {'D', 30, NULL}, + {'U', 33, NULL}, + {'U', 91, NULL}, + {'U', 43, NULL}, + {'U', 13, NULL}, + {'D', 103, NULL}, + {'U', 102, NULL}, + {'U', 83, NULL}, + {'U', 90, NULL}, + {'U', 0, NULL}, + {'U', 92, NULL}, + {'S', 11, NULL}, + {'D', 38, NULL}, + {'U', 59, NULL}, + {'U', 52, NULL}, + {'U', 114, NULL}, + {'U', 31, NULL}, + {'D', 7, NULL}, + {'U', 63, NULL}, + {'U', 104, NULL}, + {'U', 111, NULL}, + {'U', 53, NULL}, + {'U', 22, NULL}, + {'S', 130, NULL}, + {'D', 26, NULL}, + {'U', 80, NULL}, + {'U', 3, NULL}, + {'U', 125, NULL}, + {'U', 20, NULL}, + {'D', 65, NULL}, + {'U', 18, NULL}, + {'U', 96, NULL}, + {'U', 10, NULL}, + {'U', 37, NULL}, + {'U', 16, NULL}, + {'S', 101, NULL}, + {'D', 110, NULL}, + {'U', 12, NULL}, + {'U', 15, NULL}, + {'U', 55, NULL}, + {'U', 100, NULL}, + {'D', 123, NULL}, + {'U', 112, NULL}, + {'U', 40, NULL}, + {'U', 2, NULL}, + {'U', 21, NULL}, + {'U', 4, NULL}, + {'S', 47, NULL}, + {'D', 84, NULL}, + {'U', 58, NULL}, + {'U', 17, NULL}, + {'U', 60, NULL}, + {'U', 107, NULL}, + {'D', 49, NULL}, + {'U', 115, NULL}, + {'U', 126, NULL}, + {'U', 35, NULL}, + {'U', 36, NULL}, + {'U', 68, NULL}, + {'S', 93, NULL}, + {'D', 124, NULL}, + {'U', 79, NULL}, + {'U', 28, NULL}, + {'U', 14, NULL}, + {'U', 6, NULL}, + {'D', 72, NULL}, + {'U', 70, NULL}, + {'U', 86, NULL}, + {'U', 71, NULL}, + {'U', 81, NULL}, + {'U', 128, NULL}, + {'S', 97, NULL}, + {'D', 131, NULL}, + {'U', 45, NULL}, + {'U', 23, NULL}, + {'U', 50, NULL}, + {'U', 98, NULL}, + {'D', 106, NULL}, + {'U', 118, NULL}, + {'U', 77, NULL}, + {'U', 61, NULL}, + {'U', 8, NULL}, + {'U', 116, NULL}, + {'S', 108, NULL}, + {'D', 69, NULL}, + {'U', 119, NULL}, + {'U', 82, NULL}, + {'U', 74, NULL}, + {'U', 89, NULL}, + {'D', 99, NULL}, + {'U', 56, NULL}, + {'U', 109, NULL}, + {'U', 57, NULL}, + {'U', 46, NULL}, + {'U', 132, NULL}, + {'S', 44, NULL}, + {'D', 34, NULL}, + {'U', 39, NULL}, + {'U', 19, NULL}, + {'U', 85, NULL}, + {'U', 1, NULL}, + {'D', 27, NULL}, + {'U', 41, NULL}, + {'U', 5, NULL}, + {'U', 29, NULL}, + {'U', 32, NULL}, + {'U', 54, NULL}, + {'S', 25, NULL}, + {'D', 24, NULL}, + {'U', 120, NULL}, + {'U', 64, NULL}, + {'U', 117, NULL}, + {'U', 78, NULL}, + {'D', 94, NULL}, + {'U', 88, NULL}, + {'U', 127, NULL}, + {'U', 48, NULL}, + {'U', 87, NULL}, + {'U', 42, NULL}, + {'S', 75, NULL}, + {'D', 62, NULL}, + {'U', 51, NULL}, + {'U', 113, NULL}, + {'U', 73, NULL}, + {'U', 67, NULL}, + {'D', 121, NULL}, + {'U', 66, NULL}, + {'U', 122, NULL}, + {'U', 76, NULL}, + {'U', 95, NULL}, + {'U', 133, NULL}, + {'U', 105, NULL}, + {'U', 129, NULL} + } +}; diff --git a/drv/tsch/tsch.c b/drv/tsch/tsch.c index 3d2a368a7..ac2b7cb16 100644 --- a/drv/tsch/tsch.c +++ b/drv/tsch/tsch.c @@ -160,7 +160,7 @@ void db_tsch_init(node_type_t node_type, tsch_cb_t application_callback) { beacon.version = 1; beacon.type = TSCH_PACKET_TYPE_BEACON; beacon.src = db_device_id(); - _tsch_vars.max_beacons_per_slot = tsch_default_slot_timing.tx_max / ((sizeof(beacon) * BLE_2M_US_PER_BYTE) + 180); // 180 us margin + _tsch_vars.max_beacons_per_slot = 1;//tsch_default_slot_timing.tx_max / ((sizeof(beacon) * BLE_2M_US_PER_BYTE) + 180); // 180 us margin _tsch_vars.beacons_sent_this_slot = 0; // _tsch_vars.tx_time_one_beacon = (sizeof(beacon) * BLE_2M_US_PER_BYTE) + 100; // 100 us margin @@ -215,31 +215,14 @@ void _tsch_state_machine_handler(void) { // } // puts(""); - // FIXME: this code is ugly, there must be a better way. - if (_tsch_vars.node_type == NODE_TYPE_GATEWAY) { - // we want to send several beacons in the same slot, to maximize chance or reception. - // this is important because a DotBot will always be un-synced with relation to a Beacon from a Gateway it is not associated with. - if (_tsch_vars.event.slot_type == SLOT_TYPE_BEACON) { - if (_tsch_vars.beacons_sent_this_slot++ < _tsch_vars.max_beacons_per_slot) { - // stay in the same state, so that we can send still one more beacon - _set_next_state(TSCH_STATE_DO_TX); - DEBUG_GPIO_SET(&pin2); DEBUG_GPIO_CLEAR(&pin2); // Gateway sending beacon NOW - _set_timer_and_compensate(TSCH_TIMER_INTRA_SLOT_CHANNEL, TSCH_TX_TIME_ONE_BEACON, start_ts, &_tsch_state_machine_handler); - db_radio_tx(_tsch_vars.packet, _tsch_vars.packet_len); - break; - } else { - // we have sent all beacons for this slot, so we can just finish the TX phase - _set_next_state(TSCH_STATE_IS_TXING); - // _set_timer_and_compensate(TSCH_TIMER_INTRA_SLOT_CHANNEL, 100, start_ts, &_tsch_state_machine_handler); - _tsch_state_machine_handler(); - break; - } - } + if (_tsch_vars.node_type == NODE_TYPE_GATEWAY && _tsch_vars.event.slot_type == SLOT_TYPE_BEACON) { + DEBUG_GPIO_SET(&pin2); DEBUG_GPIO_CLEAR(&pin2); // Gateway sending beacon NOW } _set_next_state(TSCH_STATE_IS_TXING); _set_timer_and_compensate(TSCH_TIMER_INTRA_SLOT_CHANNEL, tsch_default_slot_timing.tx_max, start_ts, &_tsch_state_machine_handler); - db_radio_tx(_tsch_vars.packet, _tsch_vars.packet_len); + // db_radio_tx(_tsch_vars.packet, _tsch_vars.packet_len); + db_radio_tx_dispatch(); break; // in case was receiving or sending, now just finish. timeslot will begin again because of the inter-slot timer case TSCH_STATE_IS_RXING: @@ -264,9 +247,20 @@ void _handler_sm_begin_slot(void) { switch (event.radio_action) { case TSCH_RADIO_ACTION_TX: + // set the packet that will be transmitted + if (_tsch_vars.node_type == NODE_TYPE_GATEWAY && event.slot_type == SLOT_TYPE_BEACON) { + _tsch_vars.packet_len = sizeof(beacon); + memcpy(_tsch_vars.packet, &beacon, _tsch_vars.packet_len); + } else { + // TODO: get from a queue or something + _tsch_vars.packet_len = sizeof(default_packet); + memcpy(_tsch_vars.packet, default_packet, _tsch_vars.packet_len); + } + // configure radio db_radio_disable(); db_radio_set_frequency(event.frequency); + db_radio_tx_prepare(_tsch_vars.packet, _tsch_vars.packet_len); // update state _tsch_vars.event = event; @@ -276,14 +270,6 @@ void _handler_sm_begin_slot(void) { // TODO: how to get a packet? decide based on _tsch_vars.node_type and event.slot_type // could the event come with a packet? sometimes maybe? or would it be confusing? - if (_tsch_vars.node_type == NODE_TYPE_GATEWAY && event.slot_type == SLOT_TYPE_BEACON) { - _tsch_vars.packet_len = sizeof(beacon); - memcpy(_tsch_vars.packet, &beacon, _tsch_vars.packet_len); - } else { - _tsch_vars.packet_len = sizeof(default_packet); - memcpy(_tsch_vars.packet, default_packet, _tsch_vars.packet_len); - } - // set timer duration to resume again after tx_offset timer_duration = tsch_default_slot_timing.tx_offset; _set_timer_and_compensate(TSCH_TIMER_INTRA_SLOT_CHANNEL, timer_duration, start_ts, &_tsch_state_machine_handler); diff --git a/projects/01drv_tsch/01drv_tsch.c b/projects/01drv_tsch/01drv_tsch.c index 3ccfebf55..e44cf454d 100644 --- a/projects/01drv_tsch/01drv_tsch.c +++ b/projects/01drv_tsch/01drv_tsch.c @@ -47,17 +47,17 @@ schedule_t schedule_test = { } }; -extern schedule_t schedule_minuscule, schedule_small, schedule_only_beacons, schedule_only_beacons_optimized_scan; +extern schedule_t schedule_minuscule, schedule_small, schedule_huge, schedule_only_beacons, schedule_only_beacons_optimized_scan; static void radio_callback(uint8_t *packet, uint8_t length); int main(void) { // initialize schedule - schedule_t schedule = schedule_only_beacons; - node_type_t node_type = NODE_TYPE_GATEWAY; - //schedule_t schedule = schedule_small; - //node_type_t node_type = NODE_TYPE_DOTBOT; + //schedule_t schedule = schedule_only_beacons; + //node_type_t node_type = NODE_TYPE_GATEWAY; + schedule_t schedule = schedule_huge; + node_type_t node_type = NODE_TYPE_DOTBOT; db_scheduler_init(node_type, &schedule); printf("\n==== Device of type %c and id %llx is using schedule %d ====\n\n", node_type, db_device_id(), schedule.id); From 3d06459c5d05b9e5bc659b9b81a9766a8c8cf504 Mon Sep 17 00:00:00 2001 From: Geovane Fedrecheski Date: Thu, 9 Jan 2025 18:15:46 +0100 Subject: [PATCH 17/17] tsch: reduce scheduler tick time from 200 us to 6 us (!) --- drv/scheduler/scheduler.c | 6 ++---- drv/tsch/tsch.c | 4 ++-- projects/01drv_scheduler/01drv_scheduler.c | 2 ++ 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/drv/scheduler/scheduler.c b/drv/scheduler/scheduler.c index 909be41a3..99500076f 100644 --- a/drv/scheduler/scheduler.c +++ b/drv/scheduler/scheduler.c @@ -118,11 +118,9 @@ bool db_scheduler_deassign_uplink_cell(uint64_t node_id) { } tsch_radio_event_t db_scheduler_tick(uint64_t asn) { - schedule_t active_schedule = *_schedule_vars.active_schedule_ptr; - // get the current cell - size_t cell_index = asn % active_schedule.n_cells; - cell_t cell = active_schedule.cells[cell_index]; + size_t cell_index = asn % (_schedule_vars.active_schedule_ptr)->n_cells; + cell_t cell = (_schedule_vars.active_schedule_ptr)->cells[cell_index]; tsch_radio_event_t radio_event = { .radio_action = TSCH_RADIO_ACTION_SLEEP, diff --git a/drv/tsch/tsch.c b/drv/tsch/tsch.c index ac2b7cb16..0f45f1eed 100644 --- a/drv/tsch/tsch.c +++ b/drv/tsch/tsch.c @@ -53,9 +53,9 @@ gpio_t pin3 = { .port = 1, .pin = 5 }; //=========================== variables ======================================== tsch_slot_timing_t tsch_default_slot_timing = { - .rx_offset = 40 + 300, // Radio ramp-up time (40 us), + 100 us for any processing needed + .rx_offset = 40 + 200, // Radio ramp-up time (40 us), + some margin for any processing needed .rx_max = _TSCH_START_GUARD_TIME + _TSCH_PACKET_TOA_WITH_PADDING, // Guard time + Enough time to receive the maximum payload. - .tx_offset = 40 + 300 + _TSCH_START_GUARD_TIME, // Same as rx_offset, plus the guard time. + .tx_offset = 40 + 200 + _TSCH_START_GUARD_TIME, // Same as rx_offset, plus the guard time. .tx_max = _TSCH_PACKET_TOA_WITH_PADDING, // Enough to transmit the maximum payload. .end_guard = _TSCH_END_GUARD_TIME, // Extra time at the end of the slot diff --git a/projects/01drv_scheduler/01drv_scheduler.c b/projects/01drv_scheduler/01drv_scheduler.c index 06c058073..34852112f 100644 --- a/projects/01drv_scheduler/01drv_scheduler.c +++ b/projects/01drv_scheduler/01drv_scheduler.c @@ -42,7 +42,9 @@ int main(void) { uint64_t asn = 0; for (size_t j = 0; j < n_slotframes; j++) { for (size_t i = 0; i < schedule.n_cells; i++) { + uint32_t start_ts = db_timer_hf_now(TSCH_TIMER_DEV); tsch_radio_event_t event = db_scheduler_tick(asn++); + printf("Scheduler tick took %d us\n", db_timer_hf_now(TSCH_TIMER_DEV) - start_ts); printf(">> Event %c: %c, %d\n", event.slot_type, event.radio_action, event.frequency); // sleep for the duration of the slot