mirror of
https://github.com/halleysfifthinc/AVCLAN-Mockingboard.git
synced 2026-09-25 13:22:06 +00:00
Refactor peripheral/bus/phy APIs (prep for pico/PIO)
- Refactor PHY to be higher-level with a field focus (controller/peripheral address,
control byte, etc)
- Phy layer is now device address aware (ACK's automatically) (Hardens
previously ~implicit design to only support emulating one AVCLAN
unit per mockingboard build)
- Redesign `phy_active` => `phy_frame_pending` (to allow
asynchronous/buffered frame reads on pico) and embed within
Peripheral::read
- Add `deafen` function: like mute, but also ignore incoming messages (i.e.
don't receive/process at all). Makes `Bus::is_active` always return
`false`.
- Add debug bus driving functions
Assisted-by: Claude Code (Opus 5)
This commit is contained in:
@@ -147,6 +147,7 @@ struct Error {
|
|||||||
STARTBIT_MALFORMED,
|
STARTBIT_MALFORMED,
|
||||||
STARTBIT_TOO_LONG,
|
STARTBIT_TOO_LONG,
|
||||||
BAD_STARTBIT,
|
BAD_STARTBIT,
|
||||||
|
NO_FRAME,
|
||||||
POOL_EMPTY, // non-bus error
|
POOL_EMPTY, // non-bus error
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -155,6 +156,7 @@ struct Error {
|
|||||||
NAK_MESSAGE_LENGTH,
|
NAK_MESSAGE_LENGTH,
|
||||||
NAK_CONTROL,
|
NAK_CONTROL,
|
||||||
NAK_ADDRESS,
|
NAK_ADDRESS,
|
||||||
|
NAK_TOO_LONG,
|
||||||
NAK, // generic NAK has max severity
|
NAK, // generic NAK has max severity
|
||||||
BUSY,
|
BUSY,
|
||||||
MUTED,
|
MUTED,
|
||||||
|
|||||||
+95
-174
@@ -26,7 +26,8 @@
|
|||||||
| 1 │ *Acknowledge*
|
| 1 │ *Acknowledge*
|
||||||
*repeat `n` times*
|
*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 <concepts>
|
#include <concepts>
|
||||||
@@ -44,16 +45,12 @@
|
|||||||
namespace {
|
namespace {
|
||||||
using Read = avclan::detail::Error::Read;
|
using Read = avclan::detail::Error::Read;
|
||||||
using Send = avclan::detail::Error::Send;
|
using Send = avclan::detail::Error::Send;
|
||||||
using Bit = avclan::detail::Bit;
|
|
||||||
|
|
||||||
struct trailer_bits_t {};
|
// The bus spec has a unit that loses arbitration retry rather than fail: "if
|
||||||
struct no_parity_t : trailer_bits_t {}; // raw bits (the broadcast bit)
|
// the unit loses in arbitration, the frame is automatically reset up twice
|
||||||
struct with_parity_t : trailer_bits_t {}; // bits + parity (controller address)
|
// (three times in total)". Only an attempt that is outbid every time is an
|
||||||
struct with_ack_t : trailer_bits_t {
|
// error worth reporting.
|
||||||
}; // bits + parity + ACK slot (all other fields)
|
constexpr uint8_t SEND_ATTEMPTS = 3;
|
||||||
inline constexpr no_parity_t no_parity{};
|
|
||||||
inline constexpr with_parity_t with_parity{};
|
|
||||||
inline constexpr with_ack_t with_ack{};
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
namespace avclan {
|
namespace avclan {
|
||||||
@@ -72,130 +69,60 @@ public:
|
|||||||
Handle(const Handle &) = delete;
|
Handle(const Handle &) = delete;
|
||||||
Handle(Handle &&) = delete;
|
Handle(Handle &&) = delete;
|
||||||
|
|
||||||
// NOLINTNEXTLINE(readability-convert-member-functions-to-static)
|
// Forward to phy API (organized so that hal/phy.h isn't public/visible at the
|
||||||
Send sendstartbit() { return phy_send_startbit(); };
|
// C++/library level)
|
||||||
// NOLINTNEXTLINE(readability-convert-member-functions-to-static)
|
// NOLINTBEGIN(readability-convert-member-functions-to-static)
|
||||||
Read readstartbit() { return phy_read_startbit(); };
|
Read read_header(bool *is_unicast) { return phy_read_header(is_unicast); };
|
||||||
|
Read read_controller_addr(uint16_t *addr) {
|
||||||
template <auto N, std::unsigned_integral T,
|
return phy_read_controller_addr(addr);
|
||||||
std::derived_from<trailer_bits_t> Trailer>
|
|
||||||
requires(sizeof(T) < 3 && N < 16 && !std::same_as<Trailer, with_ack_t>)
|
|
||||||
Send send(T bits, Trailer /*tag*/) {
|
|
||||||
const Bit parity = sendbits<N>(bits);
|
|
||||||
|
|
||||||
if constexpr (std::is_same_v<Trailer, with_parity_t>)
|
|
||||||
sendbits<1>(to_underlying(parity));
|
|
||||||
|
|
||||||
return Send{0};
|
|
||||||
};
|
};
|
||||||
|
Read read_peripheral_addr(uint16_t *addr) {
|
||||||
template <auto N, std::unsigned_integral T>
|
return phy_read_peripheral_addr(addr);
|
||||||
requires(sizeof(T) < 3 && N < 16)
|
|
||||||
Send send(T bits, with_ack_t /*tag*/, bool expect_ack) {
|
|
||||||
send<N>(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_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 <auto N, std::unsigned_integral T,
|
Send send_header(bool is_unicast) { return phy_send_header(is_unicast); };
|
||||||
std::derived_from<trailer_bits_t> Trailer>
|
Send send_controller_addr(uint16_t addr) {
|
||||||
requires(sizeof(T) < 3 && N < 16 && !std::same_as<Trailer, with_ack_t>)
|
return phy_send_controller_addr(addr);
|
||||||
Read read(T *bits, Trailer /*tag*/) {
|
|
||||||
const Bit calc_parity = readbits<N>(bits);
|
|
||||||
if constexpr (std::is_same_v<Trailer, with_parity_t>) {
|
|
||||||
uint8_t read_parity;
|
|
||||||
readbits<1>(&read_parity);
|
|
||||||
if (to_underlying(calc_parity) != read_parity)
|
|
||||||
return Read::BAD_PARITY;
|
|
||||||
}
|
|
||||||
return Read{0};
|
|
||||||
};
|
};
|
||||||
|
Send send_peripheral_addr(uint16_t addr, bool expect_ack) {
|
||||||
template <auto N, std::unsigned_integral T, class F>
|
return phy_send_peripheral_addr(addr, expect_ack);
|
||||||
requires(sizeof(T) < 3 && N < 16)
|
|
||||||
Read read(T *bits, with_ack_t /*tag*/, F &&ack) {
|
|
||||||
if (read<N>(bits, with_parity) == Read::BAD_PARITY)
|
|
||||||
return Read::BAD_PARITY;
|
|
||||||
|
|
||||||
if (ack()) {
|
|
||||||
send_ACK();
|
|
||||||
} else {
|
|
||||||
uint8_t slot;
|
|
||||||
readbits<1>(&slot);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Read{0};
|
|
||||||
};
|
};
|
||||||
template <auto N, std::unsigned_integral T>
|
Send send_control(uint8_t control, bool expect_ack) {
|
||||||
requires(sizeof(T) < 3 && N < 16)
|
return phy_send_control(control, expect_ack);
|
||||||
Read read(T *bits, with_ack_t /*tag*/, bool ack) {
|
|
||||||
return read<N>(bits, with_ack, [=]() { return ack; });
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
static void send_ACK() { phy_send_ack(); };
|
|
||||||
static Send read_ACK() { return phy_read_ack(); };
|
|
||||||
|
|
||||||
template <auto N, class T> Bit sendbits(T bits);
|
|
||||||
template <auto N, class T> Bit readbits(T *bits);
|
|
||||||
|
|
||||||
template <auto N>
|
|
||||||
requires(N > 1 && N < 8)
|
|
||||||
Bit sendbits(uint8_t bits) {
|
|
||||||
return phy_send_bits_u8(&bits, N);
|
|
||||||
};
|
};
|
||||||
template <auto N>
|
Send send_length(uint8_t length, bool expect_ack) {
|
||||||
requires(N <= 16)
|
return phy_send_length(length, expect_ack);
|
||||||
Bit sendbits(uint16_t bits) {
|
|
||||||
return phy_send_bits_u16(&bits, N);
|
|
||||||
};
|
};
|
||||||
template <auto N>
|
Send send_data(uint8_t data, bool expect_ack) {
|
||||||
requires(N < 8)
|
return phy_send_data(data, expect_ack);
|
||||||
Bit readbits(uint8_t *bits) {
|
|
||||||
return static_cast<Bit>(phy_read_bits_u8(bits, N));
|
|
||||||
};
|
|
||||||
template <auto N>
|
|
||||||
requires(N <= 16)
|
|
||||||
Bit readbits(uint16_t *bits) {
|
|
||||||
return static_cast<Bit>(phy_read_bits_u16(bits, N));
|
|
||||||
};
|
};
|
||||||
|
// NOLINTEND(readability-convert-member-functions-to-static)
|
||||||
};
|
};
|
||||||
|
|
||||||
template <> inline Bit Bus::Handle::sendbits<8>(uint8_t bits) {
|
void Bus::init(uint16_t address) {
|
||||||
return phy_send_byte(&bits);
|
|
||||||
};
|
|
||||||
template <> inline Bit Bus::Handle::sendbits<1>(uint8_t bits) {
|
|
||||||
const Bit bit{static_cast<Bit>(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() {
|
|
||||||
// Idempotent: the single Bus is shared by reference, so every Peripheral's
|
// 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
|
// ctor calls init() on it — but the hardware must be brought up exactly once
|
||||||
// (phy_init is not assumed re-entrant/idempotent).
|
// (phy_init is not assumed re-entrant/idempotent).
|
||||||
if (inited_)
|
if (inited_)
|
||||||
return;
|
return;
|
||||||
phy_init();
|
phy_init(address);
|
||||||
muted_ = false; // phy_init leaves the bus TX unmuted
|
muted_ = false; // phy_init leaves the bus TX unmuted
|
||||||
|
deafened_ = false; // Default to listening
|
||||||
inited_ = true;
|
inited_ = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
// NOLINTNEXTLINE(readability-convert-member-functions-to-static)
|
// 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) {
|
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; }
|
||||||
|
|
||||||
auto Bus::read(uint16_t address, Frame::Print print)
|
auto Bus::read(Frame::Print print)
|
||||||
-> expected<std::unique_ptr<Frame>, Error::Read> {
|
-> expected<std::unique_ptr<Frame>, Error::Read> {
|
||||||
struct errtype {
|
struct errtype {
|
||||||
Read type;
|
Read type;
|
||||||
@@ -213,69 +140,49 @@ auto Bus::read(uint16_t address, Frame::Print print)
|
|||||||
{ // bound handle lifetime
|
{ // bound handle lifetime
|
||||||
auto handle = get();
|
auto handle = get();
|
||||||
|
|
||||||
bool shouldACK = false;
|
err.type = handle.read_header(&in->is_unicast);
|
||||||
uint8_t tmp = 0;
|
|
||||||
|
|
||||||
err.type = handle.readstartbit();
|
|
||||||
if (err.type != Read{0})
|
if (err.type != Read{0})
|
||||||
goto handle_err;
|
goto handle_err;
|
||||||
|
|
||||||
handle.read<1>(&tmp, no_parity);
|
err.type = handle.read_controller_addr(&in->controller_addr);
|
||||||
in->is_unicast = (tmp != 0U);
|
if (err.type != Read{0}) {
|
||||||
|
|
||||||
if (auto rerr = handle.read<12>(&in->controller_addr, with_parity);
|
|
||||||
rerr == BAD_PARITY) {
|
|
||||||
err.type = BAD_CONTROLLER_PARITY;
|
|
||||||
if (print.verbose)
|
if (print.verbose)
|
||||||
err.val = in->controller_addr;
|
err.val = in->controller_addr;
|
||||||
|
|
||||||
goto handle_err;
|
goto handle_err;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Using lambda for delayed evaluation of peripheral_addr field
|
err.type = handle.read_peripheral_addr(&in->peripheral_addr);
|
||||||
// deref, which will be written by the time the lambda is evaluated
|
if (err.type != Read{0}) {
|
||||||
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;
|
|
||||||
if (print.verbose)
|
if (print.verbose)
|
||||||
err.val = in->peripheral_addr;
|
err.val = in->peripheral_addr;
|
||||||
|
|
||||||
goto handle_err;
|
goto handle_err;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (auto rerr = handle.read<4>(&in->control, with_ack, shouldACK);
|
err.type = handle.read_control(&in->control);
|
||||||
rerr == BAD_PARITY) {
|
if (err.type != Read{0}) {
|
||||||
err.type = BAD_CONTROL_PARITY;
|
|
||||||
if (print.verbose)
|
if (print.verbose)
|
||||||
err.val = in->control;
|
err.val = in->control;
|
||||||
|
|
||||||
goto handle_err;
|
goto handle_err;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (auto rerr = handle.read<8>(&in->length, with_ack, shouldACK);
|
err.type = handle.read_length(&in->length);
|
||||||
rerr == BAD_PARITY) {
|
if (err.type != Read{0}) {
|
||||||
err.type = BAD_LENGTH_PARITY;
|
|
||||||
if (print.verbose)
|
if (print.verbose)
|
||||||
err.val = in->length;
|
err.val = in->length;
|
||||||
|
|
||||||
goto handle_err;
|
goto handle_err;
|
||||||
}
|
} else if (in->length == 0 || in->length > Frame::MAXLENGTH) {
|
||||||
|
|
||||||
if (in->length == 0 || in->length > Frame::MAXLENGTH) {
|
|
||||||
err.type = BAD_LENGTH_RANGE;
|
err.type = BAD_LENGTH_RANGE;
|
||||||
err.val = in->length;
|
err.val = in->length;
|
||||||
goto handle_err;
|
goto handle_err;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (uint8_t i = 0; i < in->length; i++) {
|
for (uint8_t i = 0; i < in->length; i++) {
|
||||||
if (auto rerr = handle.read<8>(&in->data[i], with_ack, shouldACK);
|
err.type = handle.read_data(&in->data[i]);
|
||||||
rerr == BAD_PARITY) {
|
if (err.type != Read{0}) {
|
||||||
err.type = BAD_DATA_PARITY;
|
|
||||||
if (print.verbose)
|
if (print.verbose)
|
||||||
err.val = in->data[i];
|
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_PARITY: fputs("reading length", stdout); goto VERBOSE;
|
||||||
case BAD_LENGTH_RANGE: printf("bad length 0x%02X:", err.val); break;
|
case BAD_LENGTH_RANGE: printf("bad length 0x%02X:", err.val); break;
|
||||||
case BAD_DATA_PARITY: fputs("reading data", stdout); goto VERBOSE;
|
case BAD_DATA_PARITY: fputs("reading data", stdout); goto VERBOSE;
|
||||||
|
case NO_FRAME:
|
||||||
case BAD_PARITY:
|
case BAD_PARITY:
|
||||||
__builtin_unreachable();
|
__builtin_unreachable();
|
||||||
VERBOSE:
|
VERBOSE:
|
||||||
@@ -343,47 +251,52 @@ auto Bus::send(const Frame &out, Frame::Print print) -> Send {
|
|||||||
goto handle_err;
|
goto handle_err;
|
||||||
}
|
}
|
||||||
|
|
||||||
{ // bound handle lifetime
|
for (uint8_t attempt = 0; attempt < SEND_ATTEMPTS; attempt++) {
|
||||||
auto handle = get();
|
auto handle = get(); // bound handle lifetime
|
||||||
|
|
||||||
if (handle.sendstartbit() == BUSY) {
|
err.type = Send{0};
|
||||||
// Some other device is already driving the bus
|
|
||||||
err.type = BUSY;
|
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;
|
goto handle_err;
|
||||||
}
|
|
||||||
|
|
||||||
handle.send<1>(static_cast<uint8_t>(out.is_unicast), no_parity);
|
err.type = handle.send_control(out.control, out.is_unicast);
|
||||||
|
if (err.type != Send{0})
|
||||||
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;
|
|
||||||
goto handle_err;
|
goto handle_err;
|
||||||
}
|
|
||||||
|
|
||||||
if (auto serr = handle.send<4>(out.control, with_ack, out.is_unicast);
|
err.type = handle.send_length(out.length, out.is_unicast);
|
||||||
serr == NAK) {
|
if (err.type != Send{0})
|
||||||
err.type = NAK_CONTROL;
|
|
||||||
goto handle_err;
|
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++) {
|
for (uint8_t i = 0; i < out.length; i++) {
|
||||||
if (auto serr = handle.send<8>(out.data[i], with_ack, out.is_unicast);
|
err.type = handle.send_data(out.data[i], out.is_unicast);
|
||||||
serr == NAK) {
|
if (err.type != Send{0}) {
|
||||||
err.type = NAK_DATA;
|
|
||||||
err.val = i;
|
err.val = i;
|
||||||
goto handle_err;
|
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
|
// back to read mode
|
||||||
if (false) { // NOLINT(readability-simplify-boolean-expr)
|
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_CONTROL:
|
||||||
case NAK_MESSAGE_LENGTH:
|
case NAK_MESSAGE_LENGTH:
|
||||||
case NAK_DATA:
|
case NAK_DATA:
|
||||||
|
case NAK_TOO_LONG:
|
||||||
case NAK:
|
case NAK:
|
||||||
fputs(" NAK: ", stdout);
|
fputs(" NAK: ", stdout);
|
||||||
switch (err.type) {
|
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_CONTROL: fputs("Control", stdout); break;
|
||||||
case NAK_MESSAGE_LENGTH: fputs("Message length", stdout); break;
|
case NAK_MESSAGE_LENGTH: fputs("Message length", stdout); break;
|
||||||
case NAK_DATA: printf(" data[%u]", err.val); break;
|
case NAK_DATA: printf(" data[%u]", err.val); break;
|
||||||
|
case NAK_TOO_LONG: fputs("too long", stdout); break;
|
||||||
case NAK:
|
case NAK:
|
||||||
case MUTED:
|
case MUTED:
|
||||||
case BUSY: __builtin_unreachable();
|
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}; };
|
Bus::Handle Bus::get() { return Handle{*this}; };
|
||||||
|
|
||||||
#if !defined(NDEBUG) && defined(MEASURE_BUS)
|
#if !defined(NDEBUG)
|
||||||
// Debug bit-timing measurement on the one physical bus; instance-scoped for the
|
|
||||||
// same reason as is_active().
|
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)
|
// NOLINTNEXTLINE(readability-convert-member-functions-to-static)
|
||||||
void Bus::measure() { phy_measure(); }
|
void Bus::measure() { phy_measure(); }
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
} // namespace avclan
|
} // namespace avclan
|
||||||
|
|||||||
+15
-3
@@ -69,18 +69,29 @@ public:
|
|||||||
Bus(const Bus &) = delete;
|
Bus(const Bus &) = delete;
|
||||||
Bus &operator=(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;
|
bool is_active() const;
|
||||||
|
|
||||||
|
// Prevent the device from being active on the bus
|
||||||
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
|
||||||
|
void deafen(bool deaf);
|
||||||
|
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
void measure();
|
void measure();
|
||||||
|
detail::Error::Send sendbyte(uint8_t byte, bool ack = false);
|
||||||
|
void set_dominant();
|
||||||
|
void set_recessive();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
expected<std::unique_ptr<Frame>, Error::Read> read(uint16_t address,
|
expected<std::unique_ptr<Frame>, Error::Read> read(Frame::Print print);
|
||||||
Frame::Print print);
|
|
||||||
Error::Send send(const Frame &out, Frame::Print print);
|
Error::Send send(const Frame &out, Frame::Print print);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -89,6 +100,7 @@ private:
|
|||||||
|
|
||||||
// Assume mute after default ctor; only viable after init call
|
// Assume mute after default ctor; only viable after init call
|
||||||
bool muted_ = true;
|
bool muted_ = true;
|
||||||
|
bool deafened_ = false;
|
||||||
bool inited_ = false;
|
bool inited_ = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
+59
-39
@@ -12,16 +12,16 @@
|
|||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
using Read = avclan::detail::Error::Read;
|
using Read = avclan::detail::Error::Read;
|
||||||
using Send = avclan::detail::Error::Send;
|
using Send = avclan::detail::Error::Send;
|
||||||
using Bit = avclan::detail::Bit;
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#else
|
#else
|
||||||
typedef enum Read Read;
|
typedef enum Read Read;
|
||||||
typedef enum Send Send;
|
typedef enum Send Send;
|
||||||
typedef enum Bit Bit;
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// One-time bring-up of the bus hardware. Leaves the bus idle and TX unmuted.
|
// 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
|
// Mute/unmute device TX. "Muted" means transmission is disabled (RX is
|
||||||
// unchanged/still allowed)
|
// unchanged/still allowed)
|
||||||
@@ -30,56 +30,76 @@ 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);
|
||||||
|
|
||||||
// True when bus is driven/"dominant" (logical 0)
|
// True when there is a frame to read. This may reflect current bus state (e.g.
|
||||||
bool phy_active(void);
|
// 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)
|
// Bus-transaction guard: quiesce the other async sources (e.g. interrupts)
|
||||||
// so that bus read/send timing isn't disturbed. Re-enable relevant async
|
// 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
|
// sources with `phy_guard_leave`.
|
||||||
// isn't a concern.
|
// - 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_enter(void);
|
||||||
void phy_guard_leave(void);
|
void phy_guard_leave(void);
|
||||||
|
|
||||||
// Validates an incoming start bit; see avclan::detail::Error::Read.
|
/* Per-field frame I/O.
|
||||||
// Invariants:
|
*
|
||||||
// - Must only be called after positive phy_active() call.
|
* Excluding the header and controller_addr send functions, all other send
|
||||||
Read phy_read_startbit(void);
|
* 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 start and broadcast bits. Always synchronous. Returns success or one of
|
||||||
Send phy_send_startbit(void);
|
// 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
|
// Send the controller address. Always synchronous. Returns success or
|
||||||
NAK. An ACK bit is a cooperative bit, where the sender starts (drives the bus)
|
// LOST_ARBITRATION (a device with a lower device is sending a frame).
|
||||||
for the sync period, and allows the receiver to drive the bus (or not) to
|
Send phy_send_controller_addr(uint16_t addr);
|
||||||
finish a "1" bit.
|
Send phy_send_peripheral_addr(uint16_t addr, bool expect_ack);
|
||||||
*/
|
Send phy_send_control(uint8_t control, bool expect_ack);
|
||||||
Send phy_read_ack(void);
|
Send phy_send_length(uint8_t length, bool expect_ack);
|
||||||
void phy_send_ack(void);
|
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;
|
// Allows asynchronous ports to block until the phy has finished sending the
|
||||||
// the read* helpers return the even parity of the bits read. The _u8/_u16
|
// frame. Returns success or the relevant field-specific NAK (e.g. NAK_ADDRESS,
|
||||||
// suffixes name the source-operand width. The function implementations need not
|
// etc) or CONTENDED_BUS. `data_index` is only written to for NAK_DATA. A fully
|
||||||
// all be separate/independent (e.g. all send functions could be redirect to a
|
// synchronous port should always report success.
|
||||||
// single phy_send_bits_u16, etc).
|
Send phy_send_done(uint8_t *data_index);
|
||||||
// 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.
|
|
||||||
|
|
||||||
// Intended for sending parity bits
|
#ifndef NDEBUG
|
||||||
void phy_send_bit(Bit bit);
|
|
||||||
|
|
||||||
// Variants available to minimize unnecessary work for max runtime efficiency
|
void phy_set_dominant(void);
|
||||||
Bit phy_send_bits_u8(const uint8_t *bits, int8_t len);
|
void phy_set_recessive(void);
|
||||||
Bit phy_send_bits_u16(const uint16_t *bits, int8_t len);
|
|
||||||
Bit phy_send_byte(const uint8_t *byte);
|
|
||||||
|
|
||||||
Bit phy_read_bits_u8(uint8_t *bits, uint8_t len);
|
#ifdef MEASURE_BUS
|
||||||
Bit phy_read_bits_u16(uint16_t *bits, int8_t len);
|
|
||||||
Bit phy_read_byte(uint8_t *byte);
|
|
||||||
|
|
||||||
#if !defined(NDEBUG) && defined(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`).
|
||||||
void phy_measure(void);
|
void phy_measure(void);
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ public:
|
|||||||
"update the sentinel value in avclan.h");
|
"update the sentinel value in avclan.h");
|
||||||
|
|
||||||
Peripheral(Bus &bus, uint16_t address) : bus{bus}, address_{address} {
|
Peripheral(Bus &bus, uint16_t address) : bus{bus}, address_{address} {
|
||||||
bus.init();
|
bus.init(address);
|
||||||
(std::get<Devs>(devices_).init(), ...);
|
(std::get<Devs>(devices_).init(), ...);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -59,7 +59,10 @@ public:
|
|||||||
|
|
||||||
expected<std::unique_ptr<Frame>, Error::Read>
|
expected<std::unique_ptr<Frame>, Error::Read>
|
||||||
read(Frame::Print print = Frame::Print{}) {
|
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<std::unique_ptr<Frame>, detail::SendError>
|
expected<std::unique_ptr<Frame>, detail::SendError>
|
||||||
|
|||||||
@@ -139,6 +139,7 @@ target_compile_definitions(jnk0le_uart
|
|||||||
PRIVATE F_CPU=${FREQSEL}
|
PRIVATE F_CPU=${FREQSEL}
|
||||||
)
|
)
|
||||||
target_compile_options(jnk0le_uart PUBLIC
|
target_compile_options(jnk0le_uart PUBLIC
|
||||||
|
$<$<CONFIG:Debug>:-Og>
|
||||||
-ffixed-r2 -ffixed-r3 -ffixed-r4) # reserved ISR scratch (SREG=r4, Z=r2:r3)
|
-ffixed-r2 -ffixed-r3 -ffixed-r4) # reserved ISR scratch (SREG=r4, Z=r2:r3)
|
||||||
target_link_libraries(avclan PUBLIC jnk0le_uart)
|
target_link_libraries(avclan PUBLIC jnk0le_uart)
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <util/atomic.h>
|
#include <util/atomic.h>
|
||||||
|
|
||||||
|
#include "avclan.h"
|
||||||
#include "hal/cd_timer.h" // statustimer_enable/disable (guard)
|
#include "hal/cd_timer.h" // statustimer_enable/disable (guard)
|
||||||
#include "hal/phy.h"
|
#include "hal/phy.h"
|
||||||
#include "media_avr.h" // media_sync_during_mask (guard)
|
#include "media_avr.h" // media_sync_during_mask (guard)
|
||||||
@@ -17,6 +18,9 @@
|
|||||||
// avr-libc.
|
// avr-libc.
|
||||||
#include "timing_avr.h" // IWYU pragma: keep
|
#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
|
// 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.
|
// decide whether to resume the TX drain (DRE interrupt) on leave.
|
||||||
extern volatile uint8_t tx0_Head, tx0_Tail;
|
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).
|
// 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
|
// Mute device TX on AVCLAN bus
|
||||||
void phy_mute(bool mute) {
|
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) {};
|
while (TCB1.CNT <= period) {};
|
||||||
}
|
}
|
||||||
|
|
||||||
void phy_send_bit(Bit bit) {
|
static void phy_send_bit(Bit bit) {
|
||||||
uint16_t zero_length, one_length;
|
uint16_t zero_length, one_length;
|
||||||
switch (bit) {
|
switch (bit) {
|
||||||
case bit_zero:
|
case bit_zero:
|
||||||
@@ -135,7 +139,7 @@ void phy_send_bit(Bit bit) {
|
|||||||
set_AVC_logic_for(1, one_length);
|
set_AVC_logic_for(1, one_length);
|
||||||
}
|
}
|
||||||
|
|
||||||
void phy_send_ack() {
|
static void phy_send_ack() {
|
||||||
TCB1.CNT = 0;
|
TCB1.CNT = 0;
|
||||||
|
|
||||||
// Wait for controller to begin ACK bit
|
// Wait for controller to begin ACK bit
|
||||||
@@ -149,7 +153,7 @@ void phy_send_ack() {
|
|||||||
phy_send_bit(bit_zero);
|
phy_send_bit(bit_zero);
|
||||||
}
|
}
|
||||||
|
|
||||||
Send phy_read_ack() {
|
static Send phy_read_ack() {
|
||||||
TCB1.CNT = 0; // Double reset of TCB1.CNT: here
|
TCB1.CNT = 0; // Double reset of TCB1.CNT: here
|
||||||
set_AVC_logic_for(0, AVCLAN_BIT1_LOGIC_0); // And here (within)
|
set_AVC_logic_for(0, AVCLAN_BIT1_LOGIC_0); // And here (within)
|
||||||
AVCLAN_setBusIdle(); // Stop driving bus
|
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
|
// Check/wait in case we get here before peripheral finishes ACK bit
|
||||||
while (!BUS_IS_IDLE) {
|
while (!BUS_IS_IDLE) {
|
||||||
if (TCB1.CNT > AVCLAN_BIT_LENGTH_MAX)
|
if (TCB1.CNT > AVCLAN_BIT_LENGTH_MAX)
|
||||||
return NAK;
|
return NAK_TOO_LONG;
|
||||||
}
|
}
|
||||||
return (Send)0;
|
return (Send)0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send `len` bits on the AVCLAN bus; returns the even parity
|
// 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 b = *bits;
|
||||||
uint8_t parity = 0;
|
uint8_t parity = 0;
|
||||||
int8_t len_mod8 = 8;
|
int8_t len_mod8 = 8;
|
||||||
@@ -189,17 +193,19 @@ Bit phy_send_bits_u8(const uint8_t *bits, int8_t len) {
|
|||||||
b <<= 1;
|
b <<= 1;
|
||||||
}
|
}
|
||||||
len_mod8 = 8;
|
len_mod8 = 8;
|
||||||
b = *--bits;
|
// Avoid under-read for last byte
|
||||||
|
if (len > 0)
|
||||||
|
b = *--bits;
|
||||||
}
|
}
|
||||||
return (parity & 1);
|
return (parity & 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send `len` bits on the AVCLAN bus; returns the even parity
|
// 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);
|
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 b = *byte;
|
||||||
uint8_t parity = 0;
|
uint8_t parity = 0;
|
||||||
|
|
||||||
@@ -238,7 +244,7 @@ ISR(TCB0_INT_vect) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Read `len` bits on the AVCLAN bus; returns the even parity
|
// 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;
|
uint8_t parity;
|
||||||
ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
|
ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
|
||||||
READING_BYTE = 0;
|
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
|
// 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;
|
uint8_t parity = 0;
|
||||||
if (len > 8) {
|
if (len > 8) {
|
||||||
uint8_t over = 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
|
// 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;
|
uint8_t parity;
|
||||||
ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
|
ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
|
||||||
READING_BYTE = 0;
|
READING_BYTE = 0;
|
||||||
@@ -304,7 +310,16 @@ Bit phy_read_byte(uint8_t *byte) {
|
|||||||
return (Bit)(parity & 1);
|
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
|
// Set pin 6 and 7 as input
|
||||||
PORTA.DIRCLR = (PIN6_bm | PIN7_bm);
|
PORTA.DIRCLR = (PIN6_bm | PIN7_bm);
|
||||||
// Disable input buffer; recommended when using AC
|
// Disable input buffer; recommended when using AC
|
||||||
@@ -339,7 +354,7 @@ void phy_init() {
|
|||||||
phy_mute(false); // unmute AVCLAN bus TX
|
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
|
// Following HAL header docs, this function is only called after a driven bus
|
||||||
// was ~recently detected.
|
// was ~recently detected.
|
||||||
// Two main (designed) entry flows, depending on timing of pulse end w.r.t.
|
// 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
|
// 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).
|
// 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
|
// wait for free line
|
||||||
TCB1.CNT = 0;
|
TCB1.CNT = 0;
|
||||||
while (BUS_IS_IDLE) {
|
while (BUS_IS_IDLE) {
|
||||||
@@ -459,6 +474,132 @@ Send phy_send_startbit() {
|
|||||||
return (Send)0;
|
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)
|
/* Disable non-read related interrupts (USART RX, RTC status tick, mic timer)
|
||||||
during AVCLAN bus transactions so framing isn't disturbed. TCB0 must remain
|
during AVCLAN bus transactions so framing isn't disturbed. TCB0 must remain
|
||||||
enabled. */
|
enabled. */
|
||||||
@@ -478,12 +619,17 @@ void phy_guard_leave() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#if !defined(NDEBUG) && defined(MEASURE_BUS)
|
#ifndef NDEBUG
|
||||||
#include <stdio.h> // phy_measure() reporting (debug builds only)
|
|
||||||
|
|
||||||
// Only used immediately below
|
void phy_set_dominant(void) { AVCLAN_setBusDriven(); }
|
||||||
#define XSTR(x) #x
|
void phy_set_recessive(void) { AVCLAN_setBusIdle(); }
|
||||||
#define STR(x) XSTR(x)
|
|
||||||
|
#ifdef MEASURE_BUS
|
||||||
|
#include <stdio.h> // 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 pulses[100];
|
||||||
static uint16_t periods[100];
|
static uint16_t periods[100];
|
||||||
@@ -516,4 +662,6 @@ void phy_measure() {
|
|||||||
|
|
||||||
phy_guard_leave();
|
phy_guard_leave();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
+16
-6
@@ -74,12 +74,10 @@ int main() {
|
|||||||
print_help();
|
print_help();
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
if (peripheral.bus_is_active()) {
|
if (auto msg = peripheral.read(Print{.print = printAllFrames,
|
||||||
if (auto msg = peripheral.read(Print{.print = printAllFrames,
|
.binary = printBinary,
|
||||||
.binary = printBinary,
|
.verbose = verbose}))
|
||||||
.verbose = verbose}))
|
incoming.push(std::move(*msg));
|
||||||
incoming.push(std::move(*msg));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (const auto *in = incoming.peek()) {
|
if (const auto *in = incoming.peek()) {
|
||||||
if (auto resp = peripheral.route(*in)) {
|
if (auto resp = peripheral.route(*in)) {
|
||||||
@@ -180,6 +178,16 @@ int main() {
|
|||||||
while (peripheral.device<CDChanger>().media_busy()) {}
|
while (peripheral.device<CDChanger>().media_busy()) {}
|
||||||
puts("end");
|
puts("end");
|
||||||
break;
|
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
|
#ifdef MEASURE_BUS
|
||||||
case 'M': peripheral.get_bus().measure(); break;
|
case 'M': peripheral.get_bus().measure(); break;
|
||||||
#endif
|
#endif
|
||||||
@@ -324,6 +332,8 @@ void print_help() {
|
|||||||
"s - MIC skip forward\n"
|
"s - MIC skip forward\n"
|
||||||
"b - MIC skip backward\n"
|
"b - MIC skip backward\n"
|
||||||
"M - Measure bit-timing (pulse-widths and periods)\n"
|
"M - Measure bit-timing (pulse-widths and periods)\n"
|
||||||
|
"+ - Set bus driven/dominant\n"
|
||||||
|
"- - Set bus idle/recessive\n"
|
||||||
#endif
|
#endif
|
||||||
"? - Print this message");
|
"? - Print this message");
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user