ProtoCore v1.0.16
Deterministic, zero-heap network stack for embedded targets
Loading...
Searching...
No Matches
interbus.h
Go to the documentation of this file.
1// ProtoCore v1.0.16 - Copyright (C) 2026 Douglas Quigg (dstroy0) <dquigg123@gmail.com>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4/**
5 * @file interbus.h
6 * @brief INTERBUS summation-frame fieldbus codec (PROTOCORE_ENABLE_INTERBUS).
7 *
8 * INTERBUS (Phoenix Contact) is a ring fieldbus with a distinctive **summation frame**: instead of
9 * addressing each device, one frame circulates the whole ring and every device is a shift-register slice
10 * of it - the master clocks the frame around, each device reads its input slot and writes its output
11 * slot as the bits pass through. A cycle frame is:
12 *
13 * [loopback word : 2][device data words...][FCS : 2 (CRC-16/CCITT)]
14 *
15 * The loopback word (0xFFFF -> 0x0000) detects the ring is closed; each device slice is a fixed number of
16 * 16-bit words (its process image). This codec assembles the summation frame from a list of per-device
17 * word slices and disassembles a received frame back into those slices, plus the CRC. The physical ring
18 * (the shift-register clocking) is hardware-gated; this is the summation-frame + process-image layer.
19 * Pure, zero heap, no stdlib, host-testable.
20 */
21
22#ifndef PROTOCORE_INTERBUS_H
23#define PROTOCORE_INTERBUS_H
24
25#include "protocore_config.h" // the entry point: protocore_types.h for the widths
26
27#if PROTOCORE_ENABLE_INTERBUS
28
30
31// This module holds nothing between calls, so it carves no borrow and states none. An entry
32// takes one all the same, and never reads it, so every namespace in the tree is invoked the
33// same way.
34
35/** @brief the loopback word that opens a summation frame. */
36#define PROTOCORE_INTERBUS_LOOPBACK 0xFFFF
37
38/** @brief What fcs takes: bytes, len. */
39typedef struct
40{
41 const uint8_t *bytes;
42 size_t len;
43} InterbusFcsArgs;
44
45/** @brief What build takes: words, word_count, out, cap. */
46typedef struct
47{
48 const uint16_t *words; ///< the concatenated device data words (big-endian on the wire)
49 size_t word_count; ///< number of 16-bit words across all device slices
50 uint8_t *out; ///< output byte buffer
51 size_t cap; ///< its capacity
52} InterbusBuildArgs;
53
54/** @brief What parse takes: frame, len, out_words, max_words, ... */
55typedef struct
56{
57 const uint8_t *frame; ///< the received frame
58 size_t len; ///< its length
59 uint16_t *out_words; ///< buffer for the decoded 16-bit words
60 size_t max_words; ///< its capacity (in words)
61 size_t *out_count; ///< set to the number of words decoded
62} InterbusParseArgs;
63
64/**
65 * @brief INTERBUS summation-frame fieldbus codec (PROTOCORE_ENABLE_INTERBUS).
66 *
67 * A caller sets the members a call takes, invokes it through ::Interbus with the bytes it runs
68 * out of, and reads the outcome off the same handle.
69 *
70 * Interbus.fcs_args.bytes = ...;
71 * Interbus.fcs_args.len = ...;
72 * Interbus.fcs(work);
73 * // Interbus.value is what the call reports
74 *
75 * @var InterbusNs::fcs_args what fcs takes: bytes, len
76 * @var InterbusNs::build_args what build takes: words, word_count, out, cap
77 * @var InterbusNs::parse_args what parse takes: frame, len, out_words, max_words,
78 * @var InterbusNs::ok true if the loopback word + FCS are valid and the words fit ...
79 * @var InterbusNs::value the value a call reports
80 * @var InterbusNs::n the frame length (2 + word_count*2 + 2), or 0 on overflow. Layout: ...
81 * @var InterbusNs::fcs CRC-16/CCITT-FALSE (the INTERBUS FCS) over len bytes
82 * @var InterbusNs::build assemble a summation frame from per-device 16-bit word slices
83 * @var InterbusNs::parse disassemble a summation frame back into device data words
84 *
85 * @c work is bytes the CALLER holds. This module reads none of them: it carries nothing
86 * between calls, so there is no state to keep and nothing to wipe. The parameter is there so
87 * a caller drives every namespace the same way.
88 */
89typedef struct
90{
91 InterbusFcsArgs fcs_args;
92 InterbusBuildArgs build_args;
93 InterbusParseArgs parse_args;
94 proto_bool ok;
95 uint16_t value;
96 size_t n;
97} InterbusVars;
98
99/** @brief The operands and the outcome. */
100extern InterbusVars InterbusV;
101
102/** @brief The entries. */
103typedef struct
104{
105 void (*const fcs)(uint8_t *work);
106 void (*const build)(uint8_t *work);
107 void (*const parse)(uint8_t *work);
108} InterbusNs;
109
110// What the table binds, defined once in the .c and taking one parameter each: everything
111// else an entry needs is an operand in InterbusV or a region of the borrow at a fixed offset.
112void protocore_interbus_fcs(uint8_t *work);
113void protocore_interbus_build(uint8_t *work);
114void protocore_interbus_parse(uint8_t *work);
115
116// `static const`, initialised HERE rather than `extern` against a definition in the .c: a
117// const object whose initializer every translation unit can see is a COMPILE-TIME FACT, so
118// `Interbus.fcs(work)` resolves to a named function and becomes a DIRECT call. An extern table
119// leaves the call indirect and the symbol live at every level, -O2 -flto included.
120static const InterbusNs Interbus __attribute__((unused)) = {
121 .fcs = protocore_interbus_fcs,
122 .build = protocore_interbus_build,
123 .parse = protocore_interbus_parse,
124};
125
127
128#endif // PROTOCORE_ENABLE_INTERBUS
129
130#endif // PROTOCORE_INTERBUS_H
#define PROTOCORE_BEGIN_DECLS
Give a header's declarations C linkage, so their symbol names carry no parameter types.
Definition types.h:96
_Bool proto_bool
The truth value.
Definition types.h:64
#define PROTOCORE_END_DECLS
Definition types.h:97