diff --git a/src/avclan/avclan.h b/src/avclan/avclan.h index a64fe15..d8db98f 100644 --- a/src/avclan/avclan.h +++ b/src/avclan/avclan.h @@ -147,6 +147,7 @@ struct Error { STARTBIT_MALFORMED, STARTBIT_TOO_LONG, BAD_STARTBIT, + NO_FRAME, POOL_EMPTY, // non-bus error }; @@ -155,6 +156,7 @@ struct Error { NAK_MESSAGE_LENGTH, NAK_CONTROL, NAK_ADDRESS, + NAK_TOO_LONG, NAK, // generic NAK has max severity BUSY, MUTED, diff --git a/src/avclan/bus.cc b/src/avclan/bus.cc index bc84822..79b1621 100644 --- a/src/avclan/bus.cc +++ b/src/avclan/bus.cc @@ -26,7 +26,8 @@ | 1 │ *Acknowledge* *repeat `n` times* - No acknowledge bits are sent for broadcast frames. + For broadcast frames the acknowledge (one) bit is sent, but an ACK response + (zero) is not expected. */ #include @@ -44,16 +45,12 @@ namespace { using Read = avclan::detail::Error::Read; using Send = avclan::detail::Error::Send; -using Bit = avclan::detail::Bit; -struct trailer_bits_t {}; -struct no_parity_t : trailer_bits_t {}; // raw bits (the broadcast bit) -struct with_parity_t : trailer_bits_t {}; // bits + parity (controller address) -struct with_ack_t : trailer_bits_t { -}; // bits + parity + ACK slot (all other fields) -inline constexpr no_parity_t no_parity{}; -inline constexpr with_parity_t with_parity{}; -inline constexpr with_ack_t with_ack{}; +// The bus spec has a unit that loses arbitration retry rather than fail: "if +// the unit loses in arbitration, the frame is automatically reset up twice +// (three times in total)". Only an attempt that is outbid every time is an +// error worth reporting. +constexpr uint8_t SEND_ATTEMPTS = 3; } // namespace namespace avclan { @@ -72,130 +69,60 @@ public: Handle(const Handle &) = delete; Handle(Handle &&) = delete; - // NOLINTNEXTLINE(readability-convert-member-functions-to-static) - Send sendstartbit() { return phy_send_startbit(); }; - // NOLINTNEXTLINE(readability-convert-member-functions-to-static) - Read readstartbit() { return phy_read_startbit(); }; - - template Trailer> - requires(sizeof(T) < 3 && N < 16 && !std::same_as) - Send send(T bits, Trailer /*tag*/) { - const Bit parity = sendbits(bits); - - if constexpr (std::is_same_v) - sendbits<1>(to_underlying(parity)); - - return Send{0}; + // Forward to phy API (organized so that hal/phy.h isn't public/visible at the + // C++/library level) + // NOLINTBEGIN(readability-convert-member-functions-to-static) + Read read_header(bool *is_unicast) { return phy_read_header(is_unicast); }; + Read read_controller_addr(uint16_t *addr) { + return phy_read_controller_addr(addr); }; - - template - requires(sizeof(T) < 3 && N < 16) - Send send(T bits, with_ack_t /*tag*/, bool expect_ack) { - send(bits, with_parity); - - if (expect_ack) - return read_ACK(); - - sendbits<1>((uint8_t)1U); // still need to fill the ack bit slot - return Send{0}; + Read read_peripheral_addr(uint16_t *addr) { + return phy_read_peripheral_addr(addr); }; + Read read_control(uint8_t *control) { return phy_read_control(control); }; + Read read_length(uint8_t *length) { return phy_read_length(length); }; + Read read_data(uint8_t *data) { return phy_read_data(data); }; - template Trailer> - requires(sizeof(T) < 3 && N < 16 && !std::same_as) - Read read(T *bits, Trailer /*tag*/) { - const Bit calc_parity = readbits(bits); - if constexpr (std::is_same_v) { - uint8_t read_parity; - readbits<1>(&read_parity); - if (to_underlying(calc_parity) != read_parity) - return Read::BAD_PARITY; - } - return Read{0}; + Send send_header(bool is_unicast) { return phy_send_header(is_unicast); }; + Send send_controller_addr(uint16_t addr) { + return phy_send_controller_addr(addr); }; - - template - requires(sizeof(T) < 3 && N < 16) - Read read(T *bits, with_ack_t /*tag*/, F &&ack) { - if (read(bits, with_parity) == Read::BAD_PARITY) - return Read::BAD_PARITY; - - if (ack()) { - send_ACK(); - } else { - uint8_t slot; - readbits<1>(&slot); - } - - return Read{0}; + Send send_peripheral_addr(uint16_t addr, bool expect_ack) { + return phy_send_peripheral_addr(addr, expect_ack); }; - template - requires(sizeof(T) < 3 && N < 16) - Read read(T *bits, with_ack_t /*tag*/, bool ack) { - return read(bits, with_ack, [=]() { return ack; }); - } - -private: - static void send_ACK() { phy_send_ack(); }; - static Send read_ACK() { return phy_read_ack(); }; - - template Bit sendbits(T bits); - template Bit readbits(T *bits); - - template - requires(N > 1 && N < 8) - Bit sendbits(uint8_t bits) { - return phy_send_bits_u8(&bits, N); + Send send_control(uint8_t control, bool expect_ack) { + return phy_send_control(control, expect_ack); }; - template - requires(N <= 16) - Bit sendbits(uint16_t bits) { - return phy_send_bits_u16(&bits, N); + Send send_length(uint8_t length, bool expect_ack) { + return phy_send_length(length, expect_ack); }; - template - requires(N < 8) - Bit readbits(uint8_t *bits) { - return static_cast(phy_read_bits_u8(bits, N)); - }; - template - requires(N <= 16) - Bit readbits(uint16_t *bits) { - return static_cast(phy_read_bits_u16(bits, N)); + Send send_data(uint8_t data, bool expect_ack) { + return phy_send_data(data, expect_ack); }; + // NOLINTEND(readability-convert-member-functions-to-static) }; -template <> inline Bit Bus::Handle::sendbits<8>(uint8_t bits) { - return phy_send_byte(&bits); -}; -template <> inline Bit Bus::Handle::sendbits<1>(uint8_t bits) { - const Bit bit{static_cast(bits & 1U)}; - phy_send_bit(bit); - return bit; -}; -template <> inline Bit Bus::Handle::readbits<8>(uint8_t *bits) { - return phy_read_byte(bits); -}; - -void Bus::init() { +void Bus::init(uint16_t address) { // Idempotent: the single Bus is shared by reference, so every Peripheral's // ctor calls init() on it — but the hardware must be brought up exactly once // (phy_init is not assumed re-entrant/idempotent). if (inited_) return; - phy_init(); - muted_ = false; // phy_init leaves the bus TX unmuted + phy_init(address); + muted_ = false; // phy_init leaves the bus TX unmuted + deafened_ = false; // Default to listening inited_ = true; }; // NOLINTNEXTLINE(readability-convert-member-functions-to-static) -bool Bus::is_active() const { return phy_active(); }; +bool Bus::is_active() const { return !deafened_ && phy_frame_pending(); }; void Bus::mute(bool mute) { phy_mute(mute); muted_ = mute; // Only update muted_ *AFTER* hardware has finished muting }; +void Bus::deafen(bool deaf) { deafened_ = deaf; } -auto Bus::read(uint16_t address, Frame::Print print) +auto Bus::read(Frame::Print print) -> expected, Error::Read> { struct errtype { Read type; @@ -213,69 +140,49 @@ auto Bus::read(uint16_t address, Frame::Print print) { // bound handle lifetime auto handle = get(); - bool shouldACK = false; - uint8_t tmp = 0; - - err.type = handle.readstartbit(); + err.type = handle.read_header(&in->is_unicast); if (err.type != Read{0}) goto handle_err; - handle.read<1>(&tmp, no_parity); - in->is_unicast = (tmp != 0U); - - if (auto rerr = handle.read<12>(&in->controller_addr, with_parity); - rerr == BAD_PARITY) { - err.type = BAD_CONTROLLER_PARITY; + err.type = handle.read_controller_addr(&in->controller_addr); + if (err.type != Read{0}) { if (print.verbose) err.val = in->controller_addr; goto handle_err; } - // Using lambda for delayed evaluation of peripheral_addr field - // deref, which will be written by the time the lambda is evaluated - auto should_ack_lambda = [&]() { - shouldACK = !is_muted() && (in->peripheral_addr == address); - return shouldACK; - }; - if (auto rerr = - handle.read<12>(&in->peripheral_addr, with_ack, should_ack_lambda); - rerr == BAD_PARITY) { - err.type = BAD_PERIPHERAL_PARITY; + err.type = handle.read_peripheral_addr(&in->peripheral_addr); + if (err.type != Read{0}) { if (print.verbose) err.val = in->peripheral_addr; goto handle_err; } - if (auto rerr = handle.read<4>(&in->control, with_ack, shouldACK); - rerr == BAD_PARITY) { - err.type = BAD_CONTROL_PARITY; + err.type = handle.read_control(&in->control); + if (err.type != Read{0}) { if (print.verbose) err.val = in->control; goto handle_err; } - if (auto rerr = handle.read<8>(&in->length, with_ack, shouldACK); - rerr == BAD_PARITY) { - err.type = BAD_LENGTH_PARITY; + err.type = handle.read_length(&in->length); + if (err.type != Read{0}) { if (print.verbose) err.val = in->length; goto handle_err; - } - - if (in->length == 0 || in->length > Frame::MAXLENGTH) { + } else if (in->length == 0 || in->length > Frame::MAXLENGTH) { err.type = BAD_LENGTH_RANGE; err.val = in->length; goto handle_err; } for (uint8_t i = 0; i < in->length; i++) { - if (auto rerr = handle.read<8>(&in->data[i], with_ack, shouldACK); - rerr == BAD_PARITY) { - err.type = BAD_DATA_PARITY; + err.type = handle.read_data(&in->data[i]); + if (err.type != Read{0}) { if (print.verbose) err.val = in->data[i]; @@ -305,6 +212,7 @@ auto Bus::read(uint16_t address, Frame::Print print) case BAD_LENGTH_PARITY: fputs("reading length", stdout); goto VERBOSE; case BAD_LENGTH_RANGE: printf("bad length 0x%02X:", err.val); break; case BAD_DATA_PARITY: fputs("reading data", stdout); goto VERBOSE; + case NO_FRAME: case BAD_PARITY: __builtin_unreachable(); VERBOSE: @@ -343,47 +251,52 @@ auto Bus::send(const Frame &out, Frame::Print print) -> Send { goto handle_err; } - { // bound handle lifetime - auto handle = get(); + for (uint8_t attempt = 0; attempt < SEND_ATTEMPTS; attempt++) { + auto handle = get(); // bound handle lifetime - if (handle.sendstartbit() == BUSY) { - // Some other device is already driving the bus - err.type = BUSY; + err.type = Send{0}; + + err.type = handle.send_header(out.is_unicast); + if (err.type != Send{0}) // BUSY or LOST_ARBITRATION (broadcast) + continue; + + err.type = handle.send_controller_addr(out.controller_addr); + if (err.type != Send{0}) // LOST_ARBITRATION: other device has lower address + continue; + + err.type = handle.send_peripheral_addr(out.peripheral_addr, out.is_unicast); + if (err.type != Send{0}) goto handle_err; - } - handle.send<1>(static_cast(out.is_unicast), no_parity); - - handle.send<12>(out.controller_addr, with_parity); - - if (auto serr = - handle.send<12>(out.peripheral_addr, with_ack, out.is_unicast); - serr == NAK) { - err.type = NAK_ADDRESS; + err.type = handle.send_control(out.control, out.is_unicast); + if (err.type != Send{0}) goto handle_err; - } - if (auto serr = handle.send<4>(out.control, with_ack, out.is_unicast); - serr == NAK) { - err.type = NAK_CONTROL; + err.type = handle.send_length(out.length, out.is_unicast); + if (err.type != Send{0}) goto handle_err; - } - - if (auto serr = handle.send<8>(out.length, with_ack, out.is_unicast); - serr == NAK) { - err.type = NAK_MESSAGE_LENGTH; - goto handle_err; - } for (uint8_t i = 0; i < out.length; i++) { - if (auto serr = handle.send<8>(out.data[i], with_ack, out.is_unicast); - serr == NAK) { - err.type = NAK_DATA; + err.type = handle.send_data(out.data[i], out.is_unicast); + if (err.type != Send{0}) { err.val = i; goto handle_err; } } - } // destroy handle + + // A phy that only queued the fields above settles them here + uint8_t data_i = 0; + err.type = phy_send_done(&data_i); + if (err.type != Send{0}) { + err.val = data_i; + goto handle_err; + } + + break; // Sent + } + + if (err.type != Send{0}) + goto handle_err; // Outbid (or busy) on every attempt // back to read mode if (false) { // NOLINT(readability-simplify-boolean-expr) @@ -396,6 +309,7 @@ auto Bus::send(const Frame &out, Frame::Print print) -> Send { case NAK_CONTROL: case NAK_MESSAGE_LENGTH: case NAK_DATA: + case NAK_TOO_LONG: case NAK: fputs(" NAK: ", stdout); switch (err.type) { @@ -403,6 +317,7 @@ auto Bus::send(const Frame &out, Frame::Print print) -> Send { case NAK_CONTROL: fputs("Control", stdout); break; case NAK_MESSAGE_LENGTH: fputs("Message length", stdout); break; case NAK_DATA: printf(" data[%u]", err.val); break; + case NAK_TOO_LONG: fputs("too long", stdout); break; case NAK: case MUTED: case BUSY: __builtin_unreachable(); @@ -420,11 +335,17 @@ auto Bus::send(const Frame &out, Frame::Print print) -> Send { Bus::Handle Bus::get() { return Handle{*this}; }; -#if !defined(NDEBUG) && defined(MEASURE_BUS) -// Debug bit-timing measurement on the one physical bus; instance-scoped for the -// same reason as is_active(). +#if !defined(NDEBUG) + +void Bus::set_dominant() { phy_set_dominant(); } +void Bus::set_recessive() { phy_set_recessive(); } + + #ifdef MEASURE_BUS +// Debug bit-timing measurement on the one physical bus; instance-scoped for +// the same reason as is_active(). // NOLINTNEXTLINE(readability-convert-member-functions-to-static) void Bus::measure() { phy_measure(); } + #endif #endif } // namespace avclan diff --git a/src/avclan/bus.hpp b/src/avclan/bus.hpp index bb2a6d9..bb43981 100644 --- a/src/avclan/bus.hpp +++ b/src/avclan/bus.hpp @@ -69,18 +69,29 @@ public: Bus(const Bus &) = delete; Bus &operator=(const Bus &) = delete; - void init(); + // `address` is our own peripheral address; the phy keeps it and acknowledges + // frames addressed to it without further instruction. One address per bus. + void init(uint16_t address); + // True when there is a frame to read and we aren't deafened. Depending on the + // target that means the bus has gone dominant or a frame is already buffered. bool is_active() const; + + // Prevent the device from being active on the bus void mute(bool mute); bool is_muted() const { return muted_; }; + // Set the device to be deaf to (ie ignore) bus activity + void deafen(bool deaf); + #ifndef NDEBUG void measure(); + detail::Error::Send sendbyte(uint8_t byte, bool ack = false); + void set_dominant(); + void set_recessive(); #endif - expected, Error::Read> read(uint16_t address, - Frame::Print print); + expected, Error::Read> read(Frame::Print print); Error::Send send(const Frame &out, Frame::Print print); private: @@ -89,6 +100,7 @@ private: // Assume mute after default ctor; only viable after init call bool muted_ = true; + bool deafened_ = false; bool inited_ = false; }; diff --git a/src/avclan/hal/phy.h b/src/avclan/hal/phy.h index 44b9d51..ee0e016 100644 --- a/src/avclan/hal/phy.h +++ b/src/avclan/hal/phy.h @@ -12,16 +12,16 @@ #ifdef __cplusplus using Read = avclan::detail::Error::Read; using Send = avclan::detail::Error::Send; -using Bit = avclan::detail::Bit; extern "C" { #else typedef enum Read Read; typedef enum Send Send; -typedef enum Bit Bit; #endif // One-time bring-up of the bus hardware. Leaves the bus idle and TX unmuted. -void phy_init(void); +// +// `address` is our device address. +void phy_init(uint16_t address); // Mute/unmute device TX. "Muted" means transmission is disabled (RX is // unchanged/still allowed) @@ -30,56 +30,76 @@ void phy_mute(bool mute); // Non-mutating (e.g. theoretically const qualified/-able) bool phy_is_muted(void); -// True when bus is driven/"dominant" (logical 0) -bool phy_active(void); +// True when there is a frame to read. This may reflect current bus state (e.g. +// a frame can be synchronously read from the bus) or indicate that a buffered +// frame is available to "read". +bool phy_frame_pending(void); // Bus-transaction guard: quiesce the other async sources (e.g. interrupts) // so that bus read/send timing isn't disturbed. Re-enable relevant async -// sources with `phy_guard_leave`. May be a no-op on a target where contention -// isn't a concern. +// sources with `phy_guard_leave`. +// - May be a no-op on a target where contention isn't a concern. +// - May acquire a hardware lock to prevent concurrent use void phy_guard_enter(void); void phy_guard_leave(void); -// Validates an incoming start bit; see avclan::detail::Error::Read. -// Invariants: -// - Must only be called after positive phy_active() call. -Read phy_read_startbit(void); +/* Per-field frame I/O. + * + * Excluding the header and controller_addr send functions, all other send + * functions may have asynchronous implementations (e.g. return before the send + * has completed on the bus). Success is indicated by a zero value `Read` or + * `Send` enum. Non-zero error codes indicate a synchronously completed send + * failure. Otherwise, `phy_send_done` must be called to block until all queued + * send's have completed, and may return the error code for a previous (queued) + * send failure; a success return value indicates that all queued send's have + * finished sending over the bus. + * + * The read functions are similarly optionally asynchronous, and may return the + * results of buffered reads. When this is the case, phy_read_header returns the + * relevant error for the entire frame. + * + * It is invalid to call any later read or send function after getting an + * error (e.g. calling `phy_read_data` after `phy_read_length` errored). + * + * `expect_ack` indicates whether the recipient should be ACK'ing; false for + * broadcast frames which don't have a single recipient. + * + */ +Read phy_read_header(bool *is_unicast); +Read phy_read_controller_addr(uint16_t *addr); +Read phy_read_peripheral_addr(uint16_t *addr); +Read phy_read_control(uint8_t *control); +Read phy_read_length(uint8_t *length); +Read phy_read_data(uint8_t *data); -// Acquire the bus and emit a start bit; may return BUSY -Send phy_send_startbit(void); +// Send start and broadcast bits. Always synchronous. Returns success or one of +// these error values: MUTED, BUSY, or LOST_ARBITRATION (if another device +// overrides our frame with a broadcast). +Send phy_send_header(bool is_unicast); -/* Returns 0 (`(Send)0`) if the peripheral sent an ACK bit, otherwise returns -NAK. An ACK bit is a cooperative bit, where the sender starts (drives the bus) -for the sync period, and allows the receiver to drive the bus (or not) to -finish a "1" bit. -*/ -Send phy_read_ack(void); -void phy_send_ack(void); +// Send the controller address. Always synchronous. Returns success or +// LOST_ARBITRATION (a device with a lower device is sending a frame). +Send phy_send_controller_addr(uint16_t addr); +Send phy_send_peripheral_addr(uint16_t addr, bool expect_ack); +Send phy_send_control(uint8_t control, bool expect_ack); +Send phy_send_length(uint8_t length, bool expect_ack); +Send phy_send_data(uint8_t data, bool expect_ack); -// Per-symbol I/O. The send* helpers return the even parity of the bits sent; -// the read* helpers return the even parity of the bits read. The _u8/_u16 -// suffixes name the source-operand width. The function implementations need not -// all be separate/independent (e.g. all send functions could be redirect to a -// single phy_send_bits_u16, etc). -// N.B: `len` is the number of bits to send. The -// C++ send/readbits templates are the only consumers and use constraints to -// enforce valid len values, so runtime checks are unnecessary. +// Allows asynchronous ports to block until the phy has finished sending the +// frame. Returns success or the relevant field-specific NAK (e.g. NAK_ADDRESS, +// etc) or CONTENDED_BUS. `data_index` is only written to for NAK_DATA. A fully +// synchronous port should always report success. +Send phy_send_done(uint8_t *data_index); -// Intended for sending parity bits -void phy_send_bit(Bit bit); +#ifndef NDEBUG -// Variants available to minimize unnecessary work for max runtime efficiency -Bit phy_send_bits_u8(const uint8_t *bits, int8_t len); -Bit phy_send_bits_u16(const uint16_t *bits, int8_t len); -Bit phy_send_byte(const uint8_t *byte); +void phy_set_dominant(void); +void phy_set_recessive(void); -Bit phy_read_bits_u8(uint8_t *bits, uint8_t len); -Bit phy_read_bits_u16(uint16_t *bits, int8_t len); -Bit phy_read_byte(uint8_t *byte); - -#if !defined(NDEBUG) && defined(MEASURE_BUS) + #ifdef MEASURE_BUS // Sample and dump bus bit timing over the serial link (REPL `M`). void phy_measure(void); + #endif #endif #ifdef __cplusplus diff --git a/src/avclan/peripheral.hpp b/src/avclan/peripheral.hpp index 1113f3e..c6697e7 100644 --- a/src/avclan/peripheral.hpp +++ b/src/avclan/peripheral.hpp @@ -40,7 +40,7 @@ public: "update the sentinel value in avclan.h"); Peripheral(Bus &bus, uint16_t address) : bus{bus}, address_{address} { - bus.init(); + bus.init(address); (std::get(devices_).init(), ...); } @@ -59,7 +59,10 @@ public: expected, Error::Read> read(Frame::Print print = Frame::Print{}) { - return bus.read(address_, print); + if (!bus.is_active()) + return unexpected{detail::Error::Read::NO_FRAME}; + + return bus.read(print); }; expected, detail::SendError> diff --git a/src/avclan/target/avr-attiny3216/CMakeLists.txt b/src/avclan/target/avr-attiny3216/CMakeLists.txt index c048aae..535fbcf 100644 --- a/src/avclan/target/avr-attiny3216/CMakeLists.txt +++ b/src/avclan/target/avr-attiny3216/CMakeLists.txt @@ -139,6 +139,7 @@ target_compile_definitions(jnk0le_uart PRIVATE F_CPU=${FREQSEL} ) target_compile_options(jnk0le_uart PUBLIC + $<$:-Og> -ffixed-r2 -ffixed-r3 -ffixed-r4) # reserved ISR scratch (SREG=r4, Z=r2:r3) target_link_libraries(avclan PUBLIC jnk0le_uart) diff --git a/src/avclan/target/avr-attiny3216/phy_avr.c b/src/avclan/target/avr-attiny3216/phy_avr.c index 230dd29..3a46954 100644 --- a/src/avclan/target/avr-attiny3216/phy_avr.c +++ b/src/avclan/target/avr-attiny3216/phy_avr.c @@ -9,6 +9,7 @@ #include #include +#include "avclan.h" #include "hal/cd_timer.h" // statustimer_enable/disable (guard) #include "hal/phy.h" #include "media_avr.h" // media_sync_during_mask (guard) @@ -17,6 +18,9 @@ // avr-libc. #include "timing_avr.h" // IWYU pragma: keep +enum bit_t : uint8_t { bit_zero = 0x00, bit_one = 0x01, bit_start = 0x10 }; +typedef enum bit_t Bit; + // USART0 TX ring indices owned by the jnk0le lib; the guard consults them to // decide whether to resume the TX drain (DRE interrupt) on leave. extern volatile uint8_t tx0_Head, tx0_Tail; @@ -80,7 +84,7 @@ bool phy_is_muted() { } // True when the bus is being driven (i.e. not idle/floating). -bool phy_active() { return (!BUS_IS_IDLE) != 0; } +bool phy_frame_pending() { return (!BUS_IS_IDLE) != 0; } // Mute device TX on AVCLAN bus void phy_mute(bool mute) { @@ -114,7 +118,7 @@ static void set_AVC_logic_for(uint8_t val, uint16_t period) { while (TCB1.CNT <= period) {}; } -void phy_send_bit(Bit bit) { +static void phy_send_bit(Bit bit) { uint16_t zero_length, one_length; switch (bit) { case bit_zero: @@ -135,7 +139,7 @@ void phy_send_bit(Bit bit) { set_AVC_logic_for(1, one_length); } -void phy_send_ack() { +static void phy_send_ack() { TCB1.CNT = 0; // Wait for controller to begin ACK bit @@ -149,7 +153,7 @@ void phy_send_ack() { phy_send_bit(bit_zero); } -Send phy_read_ack() { +static Send phy_read_ack() { TCB1.CNT = 0; // Double reset of TCB1.CNT: here set_AVC_logic_for(0, AVCLAN_BIT1_LOGIC_0); // And here (within) AVCLAN_setBusIdle(); // Stop driving bus @@ -164,13 +168,13 @@ Send phy_read_ack() { // Check/wait in case we get here before peripheral finishes ACK bit while (!BUS_IS_IDLE) { if (TCB1.CNT > AVCLAN_BIT_LENGTH_MAX) - return NAK; + return NAK_TOO_LONG; } return (Send)0; } // Send `len` bits on the AVCLAN bus; returns the even parity -Bit phy_send_bits_u8(const uint8_t *bits, int8_t len) { +static Bit phy_send_bits_u8(const uint8_t *bits, int8_t len) { uint8_t b = *bits; uint8_t parity = 0; int8_t len_mod8 = 8; @@ -189,17 +193,19 @@ Bit phy_send_bits_u8(const uint8_t *bits, int8_t len) { b <<= 1; } len_mod8 = 8; - b = *--bits; + // Avoid under-read for last byte + if (len > 0) + b = *--bits; } return (parity & 1); } // Send `len` bits on the AVCLAN bus; returns the even parity -Bit phy_send_bits_u16(const uint16_t *bits, int8_t len) { +static Bit phy_send_bits_u16(const uint16_t *bits, int8_t len) { return phy_send_bits_u8((const uint8_t *)bits + 1, len); } -Bit phy_send_byte(const uint8_t *byte) { +static Bit phy_send_byte(const uint8_t *byte) { uint8_t b = *byte; uint8_t parity = 0; @@ -238,7 +244,7 @@ ISR(TCB0_INT_vect) { } // Read `len` bits on the AVCLAN bus; returns the even parity -Bit phy_read_bits_u8(uint8_t *bits, uint8_t len) { +static Bit phy_read_bits_u8(uint8_t *bits, uint8_t len) { uint8_t parity; ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { READING_BYTE = 0; @@ -265,7 +271,7 @@ Bit phy_read_bits_u8(uint8_t *bits, uint8_t len) { } // Read `len` bits on the AVCLAN bus; returns the even parity -Bit phy_read_bits_u16(uint16_t *bits, int8_t len) { +static Bit phy_read_bits_u16(uint16_t *bits, int8_t len) { uint8_t parity = 0; if (len > 8) { uint8_t over = len - 8; @@ -278,7 +284,7 @@ Bit phy_read_bits_u16(uint16_t *bits, int8_t len) { } // Read a byte on the AVCLAN bus -Bit phy_read_byte(uint8_t *byte) { +static Bit phy_read_byte(uint8_t *byte) { uint8_t parity; ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { READING_BYTE = 0; @@ -304,7 +310,16 @@ Bit phy_read_byte(uint8_t *byte) { return (Bit)(parity & 1); } -void phy_init() { +// Our own peripheral address, and whether the frame being read is addressed to +// it. hal/phy.h puts the ACK decision in the phy, so the latch that used to +// live in Bus::read lives here now: set when the peripheral address comes in, +// and reused by every field after it. +static uint16_t own_address_; +static bool acking_; + +void phy_init(uint16_t address) { + own_address_ = address; + // Set pin 6 and 7 as input PORTA.DIRCLR = (PIN6_bm | PIN7_bm); // Disable input buffer; recommended when using AC @@ -339,7 +354,7 @@ void phy_init() { phy_mute(false); // unmute AVCLAN bus TX } -Read phy_read_startbit() { +static Read phy_read_startbit() { // Following HAL header docs, this function is only called after a driven bus // was ~recently detected. // Two main (designed) entry flows, depending on timing of pulse end w.r.t. @@ -430,7 +445,7 @@ Read phy_read_startbit() { // Acquire the bus and emit a start bit. Returns false if another device is // already driving the bus (we can't yet do proper CSMA/CD). -Send phy_send_startbit() { +static Send phy_send_startbit() { // wait for free line TCB1.CNT = 0; while (BUS_IS_IDLE) { @@ -459,6 +474,132 @@ Send phy_send_startbit() { return (Send)0; } +// Field bits, then the wire's parity bit, checked against what we counted. +static Read read_parity(Bit calc, Read bad) { + uint8_t wire = 0; + phy_read_bits_u8(&wire, 1); + return ((uint8_t)calc != wire) ? bad : (Read)0; +} + +// An acknowledge slot follows the field whether or not we drive it, so a frame +// that isn't ours still has to have the slot read away. +static void ack_slot(void) { + if (acking_) { + phy_send_ack(); + } else { + uint8_t slot = 0; + phy_read_bits_u8(&slot, 1); + } +} + +Read phy_read_header(bool *is_unicast) { + acking_ = false; // New frame; nothing is addressed to us yet + + const Read err = phy_read_startbit(); + if (err != (Read)0) + return err; + + uint8_t bit = 0; + phy_read_bits_u8(&bit, 1); // Broadcast bit: one bare bit, no parity + *is_unicast = (bit != 0); + return (Read)0; +} + +Read phy_read_controller_addr(uint16_t *addr) { + // No acknowledge slot: this field is the arbitration window. + return read_parity(phy_read_bits_u16(addr, 12), BAD_CONTROLLER_PARITY); +} + +Read phy_read_peripheral_addr(uint16_t *addr) { + const Read err = + read_parity(phy_read_bits_u16(addr, 12), BAD_PERIPHERAL_PARITY); + if (err != (Read)0) + return err; + + // The one field that decides the ack; every field after it reuses the answer. + acking_ = !phy_is_muted() && (*addr == own_address_); + ack_slot(); + return (Read)0; +} + +Read phy_read_control(uint8_t *control) { + const Read err = + read_parity(phy_read_bits_u8(control, 4), BAD_CONTROL_PARITY); + if (err != (Read)0) + return err; + ack_slot(); + return (Read)0; +} + +Read phy_read_length(uint8_t *length) { + const Read err = read_parity(phy_read_byte(length), BAD_LENGTH_PARITY); + if (err != (Read)0) + return err; + ack_slot(); + return (Read)0; +} + +Read phy_read_data(uint8_t *data) { + const Read err = read_parity(phy_read_byte(data), BAD_DATA_PARITY); + if (err != (Read)0) + return err; + ack_slot(); + return (Read)0; +} + +// `expect_ack` is "a NAK here is worth reporting", not "emit the slot": the +// slot goes out either way, and a broadcast frame simply has nobody to fill it. +static Send send_ack_slot(bool expect_ack, Send nak) { + if (expect_ack) { + const Send err = phy_read_ack(); + return err == NAK ? nak : err; + } + + const uint8_t fill = 1U; + phy_send_bits_u8(&fill, 1); + return (Send)0; +} + +Send phy_send_header(bool is_unicast) { + const Send err = phy_send_startbit(); + if (err != (Send)0) + return err; + + const uint8_t bit = (is_unicast) ? 1U : 0U; + phy_send_bits_u8(&bit, 1); // Broadcast bit: one bare bit, no parity + return (Send)0; +} + +Send phy_send_controller_addr(uint16_t addr) { + // No acknowledge slot: this field is the arbitration window. + phy_send_bit(phy_send_bits_u16(&addr, 12)); + return (Send)0; +} + +Send phy_send_peripheral_addr(uint16_t addr, bool expect_ack) { + phy_send_bit(phy_send_bits_u16(&addr, 12)); + return send_ack_slot(expect_ack, NAK_ADDRESS); +} + +Send phy_send_control(uint8_t control, bool expect_ack) { + phy_send_bit(phy_send_bits_u8(&control, 4)); + return send_ack_slot(expect_ack, NAK_CONTROL); +} + +Send phy_send_length(uint8_t length, bool expect_ack) { + phy_send_bit(phy_send_byte(&length)); + return send_ack_slot(expect_ack, NAK_MESSAGE_LENGTH); +} + +Send phy_send_data(uint8_t data, bool expect_ack) { + phy_send_bit(phy_send_byte(&data)); + return send_ack_slot(expect_ack, NAK_DATA); +} + +// Every field above is on the wire, and has reported its own outcome, by the +// time it returns: nothing is left to wait out or attribute. +Send phy_send_done([[maybe_unused]] uint8_t *data_index) { return (Send)0; } + /* Disable non-read related interrupts (USART RX, RTC status tick, mic timer) during AVCLAN bus transactions so framing isn't disturbed. TCB0 must remain enabled. */ @@ -478,12 +619,17 @@ void phy_guard_leave() { } } -#if !defined(NDEBUG) && defined(MEASURE_BUS) - #include // phy_measure() reporting (debug builds only) +#ifndef NDEBUG - // Only used immediately below - #define XSTR(x) #x - #define STR(x) XSTR(x) +void phy_set_dominant(void) { AVCLAN_setBusDriven(); } +void phy_set_recessive(void) { AVCLAN_setBusIdle(); } + + #ifdef MEASURE_BUS + #include // phy_measure() reporting (debug builds only) + + // Only used immediately below + #define XSTR(x) #x + #define STR(x) XSTR(x) static uint16_t pulses[100]; static uint16_t periods[100]; @@ -516,4 +662,6 @@ void phy_measure() { phy_guard_leave(); } + + #endif #endif diff --git a/src/sniffer.cc b/src/sniffer.cc index cf5fbfb..f26cf44 100644 --- a/src/sniffer.cc +++ b/src/sniffer.cc @@ -74,12 +74,10 @@ int main() { print_help(); while (true) { - if (peripheral.bus_is_active()) { - if (auto msg = peripheral.read(Print{.print = printAllFrames, - .binary = printBinary, - .verbose = verbose})) - incoming.push(std::move(*msg)); - } + if (auto msg = peripheral.read(Print{.print = printAllFrames, + .binary = printBinary, + .verbose = verbose})) + incoming.push(std::move(*msg)); if (const auto *in = incoming.peek()) { if (auto resp = peripheral.route(*in)) { @@ -180,6 +178,16 @@ int main() { while (peripheral.device().media_busy()) {} puts("end"); break; + case '+': + peripheral.get_bus().deafen(true); + peripheral.get_bus().set_dominant(); + puts("Set bus dominant..."); + break; + case '-': + peripheral.get_bus().set_recessive(); + peripheral.get_bus().deafen(false); + puts("Set bus recessive..."); + break; #ifdef MEASURE_BUS case 'M': peripheral.get_bus().measure(); break; #endif @@ -324,6 +332,8 @@ void print_help() { "s - MIC skip forward\n" "b - MIC skip backward\n" "M - Measure bit-timing (pulse-widths and periods)\n" + "+ - Set bus driven/dominant\n" + "- - Set bus idle/recessive\n" #endif "? - Print this message"); }