Fix intermittent, early boot short start bit errors

This commit is contained in:
Allen Hill
2026-07-21 13:22:19 -07:00
parent 909915730b
commit a99fe56aae
3 changed files with 37 additions and 9 deletions
+2 -1
View File
@@ -143,7 +143,8 @@ struct Error {
BAD_CONTROLLER_PARITY, BAD_CONTROLLER_PARITY,
BAD_CONTROL_PARITY, BAD_CONTROL_PARITY,
BAD_PARITY, // generic bad parity has max severity BAD_PARITY, // generic bad parity has max severity
STARTBIT_TOO_SHORT, STARTBIT_MISSED,
STARTBIT_MALFORMED,
STARTBIT_TOO_LONG, STARTBIT_TOO_LONG,
BAD_STARTBIT, BAD_STARTBIT,
POOL_EMPTY, // non-bus error POOL_EMPTY, // non-bus error
+5 -2
View File
@@ -289,7 +289,10 @@ auto Bus::read(uint16_t address, Frame::Print print)
switch (err.errno) { switch (err.errno) {
case POOL_EMPTY: puts("failed Frame alloc"); break; case POOL_EMPTY: puts("failed Frame alloc"); break;
case BAD_STARTBIT: fputs("bad start bit (other)", stdout); break; case BAD_STARTBIT: fputs("bad start bit (other)", stdout); break;
case STARTBIT_TOO_SHORT: fputs("bad start bit (short)", stdout); break; case STARTBIT_MISSED: fputs("missed start bit", stdout); break;
case STARTBIT_MALFORMED:
fputs("malformed start bit (external cause)", stdout);
break;
case STARTBIT_TOO_LONG: fputs("bad start bit (long)", stdout); break; case STARTBIT_TOO_LONG: fputs("bad start bit (long)", stdout); break;
case BAD_CONTROLLER_PARITY: case BAD_CONTROLLER_PARITY:
fputs("reading controller addr.", stdout); fputs("reading controller addr.", stdout);
@@ -312,7 +315,7 @@ auto Bus::read(uint16_t address, Frame::Print print)
} }
// Only print if some data has been correctly received // Only print if some data has been correctly received
if (print.print && (err.errno < STARTBIT_TOO_SHORT)) { if (print.print && (err.errno < STARTBIT_MISSED)) {
if (err.errno > BAD_DATA_PARITY) if (err.errno > BAD_DATA_PARITY)
in->length = 0; in->length = 0;
in->print(print); in->print(print);
+30 -6
View File
@@ -344,6 +344,14 @@ void phy_init() {
// error reporting; no printing happens here. // error reporting; no printing happens here.
Read phy_read_startbit() { Read phy_read_startbit() {
uint16_t startbitlen = TCB1.CNT = 0; uint16_t startbitlen = TCB1.CNT = 0;
// Reset the ~atomic `pulsewidth` variable to detect the post-pulse update
// from the TCB0_INT_vect ISR
ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
if (!BUS_IS_IDLE) // Only reset if bus is actively driven (i.e. current
pulsewidth = 0; // value is stale/already been used)
}
while (!BUS_IS_IDLE) { while (!BUS_IS_IDLE) {
startbitlen = TCB1.CNT; startbitlen = TCB1.CNT;
if (startbitlen > (uint16_t)AVCLAN_STARTBIT_LOGIC_0 * 1.2) { if (startbitlen > (uint16_t)AVCLAN_STARTBIT_LOGIC_0 * 1.2) {
@@ -367,18 +375,34 @@ Read phy_read_startbit() {
return result; return result;
} }
} }
// `pulsewidth` updates once the TCB0_INT_vect ISR runs for this pulse.
TCB1.CNT = 0;
do {
if (TCB1.CNT > (uint16_t)AVCLAN_BIT0_LOGIC_1) // Wait a max of ~6μs for ISR
return BAD_STARTBIT; // ISR/other implementation bug; abort
// Read ~atomically, to prevent torn reads
ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { startbitlen = pulsewidth; }
} while (startbitlen == 0);
if (startbitlen < (uint16_t)(AVCLAN_STARTBIT_LOGIC_0 * 0.8)) { if (startbitlen < (uint16_t)(AVCLAN_STARTBIT_LOGIC_0 * 0.8)) {
// We missed the beginning of this message; wait for it to finish (bus // Not a start bit; wait for the message to finish (bus continuously idle
// continuously idle for >1 bit length) before returning, so we don't have // for >1 bit length) before returning, so we only report one error (instead
// multiple false-starts while the in-progress message keeps sending more // of e.g. repeated "bad (short) start bit" errors)
// bits.
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; TCB1.CNT = 0; // Reset counter after each bit pulse
} }
return STARTBIT_TOO_SHORT; // 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
// device emitted a wonky pulse.
return (startbitlen < (uint16_t)AVCLAN_BIT_LENGTH_MAX) ? STARTBIT_MISSED
: STARTBIT_MALFORMED;
} }
if (startbitlen > (uint16_t)(AVCLAN_STARTBIT_LOGIC_0 * 1.2))
return STARTBIT_TOO_LONG;
return (Read)0; // that was a start bit return (Read)0; // that was a start bit
} }