Fix deafen semantics

This commit is contained in:
Allen Hill
2026-09-18 13:10:52 -07:00
parent 2c85305c0d
commit 6b22f14e6d
5 changed files with 62 additions and 7 deletions
+4 -1
View File
@@ -120,7 +120,10 @@ void Bus::mute(bool mute) {
phy_mute(mute); phy_mute(mute);
muted_ = mute; // Only update muted_ *AFTER* hardware has finished muting muted_ = mute; // Only update muted_ *AFTER* hardware has finished muting
}; };
void Bus::deafen(bool deaf) { deafened_ = deaf; } void Bus::deafen(bool deaf) {
phy_deafen(deaf);
deafened_ = deaf; // Only update deafened_ *AFTER* the phy has stopped acking
}
auto Bus::read(Frame::Print print) auto Bus::read(Frame::Print print)
-> expected<std::unique_ptr<Frame>, Error::Read> { -> expected<std::unique_ptr<Frame>, Error::Read> {
+2 -1
View File
@@ -81,7 +81,8 @@ public:
void mute(bool mute); void mute(bool mute);
bool is_muted() const { return muted_; }; bool is_muted() const { return muted_; };
// Set the device to be deaf to (ie ignore) bus activity // Set the device to be deaf to (ie don't react to) bus activity. It is
// orthogonal to mute — a deaf device can still send frames.
void deafen(bool deaf); void deafen(bool deaf);
#ifndef NDEBUG #ifndef NDEBUG
+6
View File
@@ -30,6 +30,11 @@ void phy_mute(bool mute);
// Non-mutating (e.g. theoretically const qualified/-able) // Non-mutating (e.g. theoretically const qualified/-able)
bool phy_is_muted(void); bool phy_is_muted(void);
// Withhold acknowledgement. Unlike mute this leaves TX alone: a deaf device
// can still manipulate the bus/send frames, it just never responds (e.g. ACK,
// etc). An empty implementation is sufficient for synchronous ports.
void phy_deafen(bool deaf);
// True when there is a frame to read. This may reflect current bus state (e.g. // 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 // a frame can be synchronously read from the bus) or indicate that a buffered
// frame is available to "read". // frame is available to "read".
@@ -93,6 +98,7 @@ Send phy_send_done(uint8_t *data_index);
#ifndef NDEBUG #ifndef NDEBUG
// Hold the bus at a level until the matching call.
void phy_set_dominant(void); void phy_set_dominant(void);
void phy_set_recessive(void); void phy_set_recessive(void);
+20 -2
View File
@@ -83,6 +83,10 @@ bool phy_is_muted() {
return (((VPORTA_DIR & PIN4_bm) | (VPORTA_DIR & PIN0_bm)) == 0); return (((VPORTA_DIR & PIN4_bm) | (VPORTA_DIR & PIN0_bm)) == 0);
} }
// No-op: acks are driven from inside the synchronous read path, which a deaf
// caller never enters.
void phy_deafen([[maybe_unused]] bool deaf) {}
// True when the bus is being driven (i.e. not idle/floating). // True when the bus is being driven (i.e. not idle/floating).
bool phy_frame_pending() { return (!BUS_IS_IDLE) != 0; } bool phy_frame_pending() { return (!BUS_IS_IDLE) != 0; }
@@ -621,8 +625,22 @@ void phy_guard_leave() {
#ifndef NDEBUG #ifndef NDEBUG
void phy_set_dominant(void) { AVCLAN_setBusDriven(); } // Hold the bus at a given level.
void phy_set_recessive(void) { AVCLAN_setBusIdle(); } // Overrides and restores mute state upon release (i.e. set recessive).
static void phy_set_state(bool dominant) {
static bool saved_mute = false;
if (dominant) {
saved_mute = phy_is_muted();
AVCLAN_setBusDriven();
phy_mute(false);
} else {
AVCLAN_setBusIdle();
phy_mute(saved_mute);
}
}
void phy_set_dominant(void) { phy_set_state(true); }
void phy_set_recessive(void) { phy_set_state(false); }
#ifdef MEASURE_BUS #ifdef MEASURE_BUS
#include <stdio.h> // phy_measure() reporting (debug builds only) #include <stdio.h> // phy_measure() reporting (debug builds only)
+30 -3
View File
@@ -154,6 +154,11 @@ public:
sync_ack_arming(); sync_ack_arming();
} }
void deafen(bool deaf) {
deafened_ = deaf;
sync_ack_arming();
}
// Disarm RX ACK'ing behavior; called prior to frame TX to prevent // Disarm RX ACK'ing behavior; called prior to frame TX to prevent
// self-ACK'ing. Safe to rearm any time after sending controller addr. // self-ACK'ing. Safe to rearm any time after sending controller addr.
void disarm_ack() { void disarm_ack() {
@@ -310,7 +315,7 @@ private:
// Disarming inverts the Y register (self address + parity), which leaves the // Disarming inverts the Y register (self address + parity), which leaves the
// upper 19 bits set and so prevents any read from matching. // upper 19 bits set and so prevents any read from matching.
void sync_ack_arming() { void sync_ack_arming() {
const bool arm = !muted_ && !transmitting_; const bool arm = !muted_ && !deafened_ && !transmitting_;
if (arm == ack_armed_) if (arm == ack_armed_)
return; return;
pio_sm_exec_wait_blocking(pio_, sm_, pio_encode_mov_not(pio_y, pio_y)); pio_sm_exec_wait_blocking(pio_, sm_, pio_encode_mov_not(pio_y, pio_y));
@@ -378,6 +383,7 @@ private:
bool ack_armed_ = false; // hardware: y holds self_addrp_, or its complement bool ack_armed_ = false; // hardware: y holds self_addrp_, or its complement
bool muted_ = false; // we don't answer on the bus bool muted_ = false; // we don't answer on the bus
bool deafened_ = false; // we don't answer, but may still transmit
bool transmitting_ = false; // our own frame is on the wire bool transmitting_ = false; // our own frame is on the wire
std::array<RxFrame, RXQ_N> rxq_ = {}; std::array<RxFrame, RXQ_N> rxq_ = {};
@@ -431,6 +437,7 @@ public:
// Claims and configures both SMs, but doesn't start them. // Claims and configures both SMs, but doesn't start them.
void init(PIO pio, uint pin_rx, uint pin_tx) { void init(PIO pio, uint pin_rx, uint pin_tx) {
pio_ = pio; pio_ = pio;
pin_tx_ = pin_tx;
tx_offset_ = (uint)pio_add_program(pio_, &iebus_tx_program); tx_offset_ = (uint)pio_add_program(pio_, &iebus_tx_program);
ack_offset_ = (uint)pio_add_program(pio_, &iebus_ack_program); ack_offset_ = (uint)pio_add_program(pio_, &iebus_ack_program);
tx_sm_ = (uint)pio_claim_unused_sm(pio_, true); tx_sm_ = (uint)pio_claim_unused_sm(pio_, true);
@@ -474,6 +481,23 @@ public:
bool is_muted() const { return muted_; } bool is_muted() const { return muted_; }
// Hold the bus at a given level.
// Overrides and restores mute state upon release (i.e. set recessive).
void set_state(bool dominant) {
static bool saved_mute = false;
if (dominant) {
saved_mute = muted_;
mute(true);
pio_sm_set_pins_with_mask(pio_, tx_sm_, 0U, 1U << pin_tx_);
} else {
pio_sm_set_pins_with_mask(pio_, tx_sm_, 1U << pin_tx_, 1U << pin_tx_);
// The ack SM parks on `wait 1 irq`, which consumes a flag raised while it
// was down and drives the slot immediately -- Phy::mute's ordering.
pio_interrupt_clear(pio_, ack_irq);
mute(saved_mute);
}
}
// Send start and broadcast bits. // Send start and broadcast bits.
Send send_header(bool is_unicast) { Send send_header(bool is_unicast) {
if (muted_) if (muted_)
@@ -624,6 +648,7 @@ private:
IEBusRx &rx_; IEBusRx &rx_;
PIO pio_; PIO pio_;
uint pin_tx_;
uint tx_sm_; uint tx_sm_;
uint tx_offset_; uint tx_offset_;
uint ack_sm_; uint ack_sm_;
@@ -768,6 +793,8 @@ extern "C" void phy_mute(bool mute) { phy.mute(mute); }
extern "C" bool phy_is_muted() { return phy.is_muted(); } extern "C" bool phy_is_muted() { return phy.is_muted(); }
extern "C" void phy_deafen(bool deaf) { phy.rx().deafen(deaf); }
extern "C" bool phy_frame_pending() { return phy.rx().frame_pending(); } extern "C" bool phy_frame_pending() { return phy.rx().frame_pending(); }
extern "C" void phy_guard_enter() {} extern "C" void phy_guard_enter() {}
@@ -852,8 +879,8 @@ extern "C" Send phy_send_done(uint8_t *data_index) {
#ifndef NDEBUG #ifndef NDEBUG
void phy_set_dominant() {} extern "C" void phy_set_dominant() { phy.tx().set_state(true); }
void phy_set_recessive() {} extern "C" void phy_set_recessive() { phy.tx().set_state(false); }
#ifdef MEASURE_BUS #ifdef MEASURE_BUS
// Sample and dump bus bit timing over the serial link (REPL `M`). // Sample and dump bus bit timing over the serial link (REPL `M`).