mirror of
https://github.com/halleysfifthinc/AVCLAN-Mockingboard.git
synced 2026-08-25 06:45:33 +00:00
Improve correctness of phy_read_startbit invariants/assumptions
This commit is contained in:
@@ -40,12 +40,12 @@ bool phy_active(void);
|
|||||||
void phy_guard_enter(void);
|
void phy_guard_enter(void);
|
||||||
void phy_guard_leave(void);
|
void phy_guard_leave(void);
|
||||||
|
|
||||||
// Start-bit handling, factored out of read/sendframe so the framing layer holds
|
// Validates an incoming start bit; see avclan::detail::Error::Read.
|
||||||
// no bus-timing or hardware-recovery logic.
|
// Invariants:
|
||||||
// - phy_read_startbit waits for and validates an incoming start bit, doing
|
// - Must only be called after positive phy_active() call.
|
||||||
// any target-specific bus recovery; see avclan::detail::Error::Read.
|
|
||||||
// - phy_send_startbit acquires the bus and emits a start bit; may return BUSY
|
|
||||||
Read phy_read_startbit(void);
|
Read phy_read_startbit(void);
|
||||||
|
|
||||||
|
// Acquire the bus and emit a start bit; may return BUSY
|
||||||
Send phy_send_startbit(void);
|
Send phy_send_startbit(void);
|
||||||
|
|
||||||
/* Returns 0 (`(Send)0`) if the peripheral sent an ACK bit, otherwise returns
|
/* Returns 0 (`(Send)0`) if the peripheral sent an ACK bit, otherwise returns
|
||||||
|
|||||||
@@ -341,23 +341,36 @@ void phy_init() {
|
|||||||
phy_mute(false); // unmute AVCLAN bus TX
|
phy_mute(false); // unmute AVCLAN bus TX
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wait for and validate an incoming start bit. On an over-long "driven" bus
|
|
||||||
// (AC2 latched high because the bus is actually floating) this kicks PA7 hard
|
|
||||||
// high to unlatch the comparator. The framing layer maps the result to its own
|
|
||||||
// error reporting; no printing happens here.
|
|
||||||
Read phy_read_startbit() {
|
Read phy_read_startbit() {
|
||||||
uint16_t startbitlen = TCB1.CNT = 0;
|
// 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.
|
||||||
|
// beginning this function:
|
||||||
|
// - Bus is still driven or ISR is pending
|
||||||
|
// - Wait for pulse to end if needed
|
||||||
|
// - On an over-long "driven" bus (AC2 latched high when bus is floating)
|
||||||
|
// kick PA7 hard high to try unlatching the comparator
|
||||||
|
// - Grab updated `pulsewidth`
|
||||||
|
// - Bus is idle
|
||||||
|
// - `pulsewidth` holds duration of most recent pulse.
|
||||||
|
|
||||||
// Reset the ~atomic `pulsewidth` variable to detect the post-pulse update
|
TCB1.CNT = 0;
|
||||||
// from the TCB0_INT_vect ISR
|
|
||||||
|
// "stale"ness indicates that the current `pulsewidth` value is out of date
|
||||||
|
bool stale = true;
|
||||||
|
|
||||||
|
uint16_t startbitlen;
|
||||||
ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
|
ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
|
||||||
if (!BUS_IS_IDLE) // Only reset if bus is actively driven (i.e. current
|
// pulsewidth won't/can't be updated during atomic block, but pulse may end
|
||||||
pulsewidth = 0; // value is stale/already been used)
|
// and store an IRQ. If bus is idle and no pending ISR, then pulsewidth
|
||||||
|
// currrently holds the duration of the most recent pulse (stale = false).
|
||||||
|
stale = (((!BUS_IS_IDLE) | (TCB0.INTFLAGS & TCB_CAPT_bm)) != 0);
|
||||||
|
// Read ~atomically, to prevent torn reads
|
||||||
|
startbitlen = pulsewidth;
|
||||||
}
|
}
|
||||||
|
|
||||||
while (!BUS_IS_IDLE) {
|
while (!BUS_IS_IDLE) {
|
||||||
startbitlen = TCB1.CNT;
|
if (TCB1.CNT > (uint16_t)AVCLAN_STARTBIT_LOGIC_0 * 1.2) {
|
||||||
if (startbitlen > (uint16_t)AVCLAN_STARTBIT_LOGIC_0 * 1.2) {
|
|
||||||
Read result = STARTBIT_TOO_LONG;
|
Read result = STARTBIT_TOO_LONG;
|
||||||
while (!BUS_IS_IDLE) {
|
while (!BUS_IS_IDLE) {
|
||||||
// If bus is "driven" too long, assume the AC2 is latched (e.g.
|
// If bus is "driven" too long, assume the AC2 is latched (e.g.
|
||||||
@@ -379,15 +392,23 @@ Read phy_read_startbit() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// `pulsewidth` updates once the TCB0_INT_vect ISR runs for this pulse.
|
|
||||||
TCB1.CNT = 0;
|
TCB1.CNT = 0;
|
||||||
do {
|
while (stale) { // bus was driven, or a capture was still unconsumed, at entry
|
||||||
if (TCB1.CNT > (uint16_t)AVCLAN_BIT0_LOGIC_1) // Wait a max of ~6μs for ISR
|
uint16_t old_pulsewidth = startbitlen;
|
||||||
return BAD_STARTBIT; // ISR/other implementation bug; abort
|
// `pulsewidth` updates once the TCB0_INT_vect ISR runs for this pulse.
|
||||||
|
|
||||||
// Read ~atomically, to prevent torn reads
|
// Read ~atomically, to prevent torn reads
|
||||||
ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { startbitlen = pulsewidth; }
|
ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { startbitlen = pulsewidth; }
|
||||||
} while (startbitlen == 0);
|
if (startbitlen != old_pulsewidth)
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Exit for potential false-negative: ISR writes for identical mid-frame
|
||||||
|
// bits (e.g. double 0 or 1 bits) can't be detected, but the ISR should
|
||||||
|
// definitely have run after ~6μs (i.e. startbitlen is actually current).
|
||||||
|
// The delay is ultimately free since mid-frame entry is directed to the
|
||||||
|
// "wait-for-message-completion" stall loop anyways
|
||||||
|
if (TCB1.CNT > (uint16_t)AVCLAN_BIT0_LOGIC_1)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if (startbitlen < (uint16_t)(AVCLAN_STARTBIT_LOGIC_0 * 0.8)) {
|
if (startbitlen < (uint16_t)(AVCLAN_STARTBIT_LOGIC_0 * 0.8)) {
|
||||||
// Not a start bit; wait for the message to finish (bus continuously idle
|
// Not a start bit; wait for the message to finish (bus continuously idle
|
||||||
@@ -396,7 +417,7 @@ Read phy_read_startbit() {
|
|||||||
TCB1.CNT = 0;
|
TCB1.CNT = 0;
|
||||||
while (TCB1.CNT < (uint16_t)(AVCLAN_BIT_LENGTH_MAX * 1.2)) {
|
while (TCB1.CNT < (uint16_t)(AVCLAN_BIT_LENGTH_MAX * 1.2)) {
|
||||||
if (!BUS_IS_IDLE)
|
if (!BUS_IS_IDLE)
|
||||||
TCB1.CNT = 0; // Reset counter after each bit pulse
|
TCB1.CNT = 0; // (Re)start count from when bus was last driven
|
||||||
}
|
}
|
||||||
// A pulse no wider than a normal bit means we merely tuned in mid-frame and
|
// A pulse no wider than a normal bit means we merely tuned in mid-frame and
|
||||||
// this was a data bit; a wider-but-still-sub-start pulse means some other
|
// this was a data bit; a wider-but-still-sub-start pulse means some other
|
||||||
|
|||||||
Reference in New Issue
Block a user