From 0b2eba7eea6e5a2826c2ea3d1c22420f29251565 Mon Sep 17 00:00:00 2001 From: Kalyan Sriram Date: Mon, 19 Jul 2021 16:31:04 -0700 Subject: [PATCH] add acceptance filter configuration helpers Adds helper API functions for configuring CAN controller hardware acceptance filters. --- README.md | 22 +++++++++++++++- libcanard/canard.c | 44 ++++++++++++++++++++++++++++++++ libcanard/canard.h | 62 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 127 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index de25dd9..b1a70bd 100644 --- a/README.md +++ b/README.md @@ -206,10 +206,28 @@ else } ``` +A simple API for generating CAN hardware acceptance filter configurations is also provided. +Acceptance filters are generated in an extended 29-bit ID + mask scheme and can be used to minimize the number of irrelevant transfers processed in software. + +```c +// Generate an acceptance filter to receive only uavcan.node.Heartbeat.1.0 messages (fixed port ID 7509): +CanardAcceptanceFilterConfig heartbeat_config = canardMakeAcceptanceFilterConfigForSubject(7509); +CanardAcceptanceFilterConfig register_access_config = canardMakeAcceptanceFilterConfigForService(384, ins.node_id); + +// You can also combine the two filter configurations into one (may also accept in other messages). +// This allows consolidating a large set of configurations to fit the number of hardware filters. +// For more information on the optimal subset of configurations to consolidate to minimize wasted CPU, +// see the UAVCAN specification. +CanardAcceptanceFilterConfig combined_config = canardConsolidateAcceptanceFilterConfigs(&heartbeat_config, ®ister_access_config); +configureHardwareFilters(combined_config.extended_can_id, combined_config.extended_mask); +``` + +Full API specification is available in the documentation. +If you find the examples to be unclear or incorrect, please, open a ticket. + ## Revisions ### v2.0 - - Dedicated transmission queues per redundant CAN interface with depth limits. The application is now expected to instantiate `CanardTxQueue` (or several in case of redundant transport) manually. @@ -225,6 +243,8 @@ else - Support build configuration headers via `CANARD_CONFIG_HEADER`. +- Add API for generating CAN hardware acceptance filter configurations + ### v1.1 - Add new API function `canardRxAccept2()`, deprecate `canardRxAccept()`. diff --git a/libcanard/canard.c b/libcanard/canard.c index 52e981a..6219bbb 100644 --- a/libcanard/canard.c +++ b/libcanard/canard.c @@ -1165,3 +1165,47 @@ int8_t canardRxUnsubscribe(CanardInstance* const ins, } return out; } + +CanardAcceptanceFilterConfig canardMakeAcceptanceFilterConfigForSubject(const CanardPortID subject_id) +{ + CanardAcceptanceFilterConfig out = {0}; + + out.extended_can_id = subject_id << OFFSET_SUBJECT_ID; + out.extended_mask = FLAG_SERVICE_NOT_MESSAGE | FLAG_RESERVED_07 | (CANARD_SUBJECT_ID_MAX << OFFSET_SUBJECT_ID); + + return out; +} + +CanardAcceptanceFilterConfig canardMakeAcceptanceFilterConfigForService(const CanardPortID service_id, + const CanardNodeID local_node_id) +{ + CanardAcceptanceFilterConfig out = {0}; + + out.extended_can_id = + FLAG_SERVICE_NOT_MESSAGE | (service_id << OFFSET_SERVICE_ID) | (local_node_id << OFFSET_DST_NODE_ID); + out.extended_mask = FLAG_SERVICE_NOT_MESSAGE | FLAG_RESERVED_23 | (CANARD_SERVICE_ID_MAX << OFFSET_SERVICE_ID) | + (CANARD_NODE_ID_MAX << OFFSET_DST_NODE_ID); + + return out; +} + +CanardAcceptanceFilterConfig canardMakeAcceptanceFilterConfigForServices(const CanardNodeID local_node_id) +{ + CanardAcceptanceFilterConfig out = {0}; + + out.extended_can_id = FLAG_SERVICE_NOT_MESSAGE | (local_node_id << OFFSET_DST_NODE_ID); + out.extended_mask = FLAG_SERVICE_NOT_MESSAGE | FLAG_RESERVED_23 | (CANARD_NODE_ID_MAX << OFFSET_DST_NODE_ID); + + return out; +} + +CanardAcceptanceFilterConfig canardConsolidateAcceptanceFilterConfigs(const CanardAcceptanceFilterConfig* a, + const CanardAcceptanceFilterConfig* b) +{ + CanardAcceptanceFilterConfig out = {0}; + + out.extended_mask = a->extended_mask & b->extended_mask & ~(a->extended_can_id ^ b->extended_can_id); + out.extended_can_id = a->extended_can_id & out.extended_mask; + + return out; +} diff --git a/libcanard/canard.h b/libcanard/canard.h index 39f4be1..06474b1 100644 --- a/libcanard/canard.h +++ b/libcanard/canard.h @@ -383,6 +383,20 @@ struct CanardInstance CanardTreeNode* rx_subscriptions[CANARD_NUM_TRANSFER_KINDS]; }; +/// CAN acceptance filter configuration with an extended 29-bit ID utilizing an ID + mask filter scheme. +/// Filter configuration can be programmed into a CAN controller to filter out irrelevant messages in hardware. +/// This allows the software application to reduce CPU load spent on processing irrelevant messages. +typedef struct CanardAcceptanceFilterConfig +{ + /// 29-bit extended ID. Defines the extended CAN ID to filter incoming frames against. + /// The bits above 29-th shall be zero. + uint32_t extended_can_id; + /// 29-bit extended mask. Defines the bitmask used to enable/disable bits used to filter messages. + /// Only bits that are enabled are compared to the extended_can_id for filtering. + /// The bits above 29-th shall be zero. + uint32_t extended_mask; +} CanardAcceptanceFilterConfig; + /// Construct a new library instance. /// The default values will be assigned as specified in the structure field documentation. /// If any of the pointers are NULL, the behavior is undefined. @@ -635,6 +649,54 @@ int8_t canardRxUnsubscribe(CanardInstance* const ins, const CanardTransferKind transfer_kind, const CanardPortID port_id); +/// Generate an acceptance filter configuration to accept a specific subject ID. +/// +/// Complex applications will likely subscribe to more subject IDs than there are +/// acceptance filters available in the CAN hardware. In this case, the application +/// should implement filter consolidation. See canardConsolidateAcceptanceFilterConfigs() +/// as well as the UAVCAN specification for details. +CanardAcceptanceFilterConfig canardMakeAcceptanceFilterConfigForSubject(const CanardPortID subject_id); + +/// Generate an acceptance filter configuration to accept a specific service. +/// +/// Complex applications will likely subscribe to more service IDs than there are +/// acceptance filters available in the CAN hardware. In this case, the application +/// should implement filter consolidation. See canardConsolidateAcceptanceFilterConfigs() +/// as well as the UAVCAN specification for details. +/// +/// Users may prefer to instead use a catch-all acceptance filter configuration for accepting +/// all service requests and responses targeted at the specified local node ID. +/// See canardMakeAcceptanceFilterConfigForServices() for this. +CanardAcceptanceFilterConfig canardMakeAcceptanceFilterConfigForService(const CanardPortID service_id, + const CanardNodeID local_node_id); + +/// Generate an acceptance filter configuration to accept all service +/// requests or responses targeted to the specified local node ID. +/// +/// Due to the relatively low frequency of service transfers expected on a network, +/// and the fact that a service directed at a specific node is not likely to be rejected by that node, +/// a user may prefer to use this over canardMakeAcceptanceFilterConfigForService() +/// in order to simplify the API usage and reduce the number of required hardware CAN acceptance filters. +CanardAcceptanceFilterConfig canardMakeAcceptanceFilterConfigForServices(const CanardNodeID local_node_id); + +/// Consolidate two acceptance filter configurations into a single configuration. +/// +/// Complex applications will likely subscribe to more subject IDs than there are +/// acceptance filters available in the CAN hardware. In this case, the application +/// should implement filter consolidation. While this may make it impossible to create +/// a 'perfect' filter that only accepts desired subject IDs, the application should apply +/// consolidation in a manner that minimizes the number of undesired messages that pass +/// through the hardware acceptance filters and require software filtering (implemented by canardRxSubscribe). +/// +/// While optimal choice of filter consolidation is a function of the number of available hardware filters, +/// the set of transfers needed by the application, and the expected frequency of occurence +/// of all possible distinct transfers on the bus, it is possible to generate a quasi-optimal configuration +/// if information about the frequency of occurence of different transfers is not known. +/// For details, see the "Automatic hardware acceptance filter configuration" note under the UAVCAN/CAN section +/// in the Transport Layer chapter of the UAVCAN specification. +CanardAcceptanceFilterConfig canardConsolidateAcceptanceFilterConfigs(const CanardAcceptanceFilterConfig* a, + const CanardAcceptanceFilterConfig* b); + #ifdef __cplusplus } #endif