lib: Resync can2040 code to latest upstream

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
This commit is contained in:
Kevin O'Connor 2022-12-15 13:51:23 -05:00 committed by Eric Callahan
parent 2b22438c01
commit b19cc2a9ad
3 changed files with 150 additions and 113 deletions

View File

@ -122,3 +122,7 @@ has been modfied to enable the US code page (437) and enable label
support. The file "diskio.c" in the original archive is not included
as it is only serves as an example for how to implement the media api
callbacks.
The can2040 directory contains code from:
https://github.com/KevinOConnor/can2040
revision 177b0073fe6f19281ee7f7fdbe9599e32d1b4b8b.

View File

@ -116,6 +116,13 @@ static const uint16_t can2040_program_instructions[] = {
0xc027, // 31: irq wait 7
};
// Local names for PIO state machine IRQs
#define SI_MAYTX PIO_IRQ0_INTE_SM0_BITS
#define SI_MATCHED PIO_IRQ0_INTE_SM2_BITS
#define SI_ACKDONE PIO_IRQ0_INTE_SM3_BITS
#define SI_RX_DATA PIO_IRQ0_INTE_SM1_RXNEMPTY_BITS
#define SI_TXPENDING PIO_IRQ0_INTE_SM1_BITS // Misc bit manually forced
// Setup PIO "sync" state machine (state machine 0)
static void
pio_sync_setup(struct can2040 *cd)
@ -213,14 +220,6 @@ pio_rx_check_stall(struct can2040 *cd)
return pio_hw->fdebug & (1 << (PIO_FDEBUG_RXSTALL_LSB + 1));
}
// Report number of bytes still pending in PIO "rx" fifo queue
static int
pio_rx_fifo_level(struct can2040 *cd)
{
pio_hw_t *pio_hw = cd->pio_hw;
return (pio_hw->flevel & PIO_FLEVEL_RX1_BITS) >> PIO_FLEVEL_RX1_LSB;
}
// Set PIO "match" state machine to raise a "matched" signal on a bit sequence
static void
pio_match_check(struct can2040 *cd, uint32_t match_key)
@ -250,7 +249,7 @@ pio_tx_reset(struct can2040 *cd)
pio_hw_t *pio_hw = cd->pio_hw;
pio_hw->ctrl = ((0x07 << PIO_CTRL_SM_ENABLE_LSB)
| (0x08 << PIO_CTRL_SM_RESTART_LSB));
pio_hw->irq = (1 << 2) | (1<< 3); // clear "matched" and "ack done" signals
pio_hw->irq = (SI_MATCHED | SI_ACKDONE) >> 8; // clear PIO irq flags
// Clear tx fifo
struct pio_sm_hw *sm = &pio_hw->sm[3];
sm->shiftctrl = 0;
@ -295,54 +294,49 @@ pio_tx_inject_ack(struct can2040 *cd, uint32_t match_key)
pio_match_check(cd, match_key);
}
// Check if the PIO "tx" state machine stopped due to passive/dominant conflict
// Did PIO "tx" state machine unexpectedly finish a transmit attempt?
static int
pio_tx_did_conflict(struct can2040 *cd)
pio_tx_did_fail(struct can2040 *cd)
{
pio_hw_t *pio_hw = cd->pio_hw;
return pio_hw->sm[3].addr == can2040_offset_tx_conflict;
// Check for passive/dominant bit conflict without parser noticing
if (pio_hw->sm[3].addr == can2040_offset_tx_conflict)
return !(pio_hw->intr & SI_RX_DATA);
// Check for unexpected drain of transmit queue without parser noticing
return (!(pio_hw->flevel & PIO_FLEVEL_TX3_BITS)
&& (pio_hw->intr & (SI_MAYTX | SI_RX_DATA)) == SI_MAYTX);
}
// Enable host irq on a "may transmit" signal (sm irq 0)
// Enable host irqs for state machine signals
static void
pio_irq_set_maytx(struct can2040 *cd)
pio_irq_set(struct can2040 *cd, uint32_t sm_irqs)
{
pio_hw_t *pio_hw = cd->pio_hw;
pio_hw->inte0 = PIO_IRQ0_INTE_SM0_BITS | PIO_IRQ0_INTE_SM1_RXNEMPTY_BITS;
pio_hw->inte0 = sm_irqs | SI_RX_DATA;
}
// Enable host irq on a "may transmit" or "matched" signal (sm irq 0 or 2)
static void
pio_irq_set_maytx_matched(struct can2040 *cd)
// Return current host irq mask
static uint32_t
pio_irq_get(struct can2040 *cd)
{
pio_hw_t *pio_hw = cd->pio_hw;
pio_hw->inte0 = (PIO_IRQ0_INTE_SM0_BITS | PIO_IRQ0_INTE_SM2_BITS
| PIO_IRQ0_INTE_SM1_RXNEMPTY_BITS);
return pio_hw->inte0;
}
// Enable host irq on a "may transmit" or "ack done" signal (sm irq 0 or 3)
// Raise the txpending flag
static void
pio_irq_set_maytx_ackdone(struct can2040 *cd)
pio_signal_set_txpending(struct can2040 *cd)
{
pio_hw_t *pio_hw = cd->pio_hw;
pio_hw->inte0 = (PIO_IRQ0_INTE_SM0_BITS | PIO_IRQ0_INTE_SM3_BITS
| PIO_IRQ0_INTE_SM1_RXNEMPTY_BITS);
pio_hw->irq_force = SI_TXPENDING >> 8;
}
// Atomically enable "may transmit" signal (sm irq 0)
// Clear the txpending flag
static void
pio_irq_atomic_set_maytx(struct can2040 *cd)
pio_signal_clear_txpending(struct can2040 *cd)
{
pio_hw_t *pio_hw = cd->pio_hw;
hw_set_bits(&pio_hw->inte0, PIO_IRQ0_INTE_SM0_BITS);
}
// Disable PIO host irqs (except for normal data read irq)
static void
pio_irq_set_none(struct can2040 *cd)
{
pio_hw_t *pio_hw = cd->pio_hw;
pio_hw->inte0 = PIO_IRQ0_INTE_SM1_RXNEMPTY_BITS;
pio_hw->irq = SI_TXPENDING >> 8;
}
// Setup PIO state machines
@ -353,6 +347,8 @@ pio_sm_setup(struct can2040 *cd)
pio_hw_t *pio_hw = cd->pio_hw;
pio_hw->ctrl = PIO_CTRL_SM_RESTART_BITS | PIO_CTRL_CLKDIV_RESTART_BITS;
pio_hw->fdebug = 0xffffffff;
pio_hw->irq = 0xff;
pio_signal_set_txpending(cd);
// Load pio program
int i;
@ -478,6 +474,14 @@ unstuf_clear_state(struct can2040_bitunstuffer *bu)
bu->stuffed_bits = (bu->stuffed_bits & (lb - 1)) | (lb << 1);
}
// Restore raw bitstuffing state (used to undo unstuf_clear_state() )
static void
unstuf_restore_state(struct can2040_bitunstuffer *bu, uint32_t data)
{
uint32_t cs = bu->count_stuff;
bu->stuffed_bits = (bu->stuffed_bits & ((1 << cs) - 1)) | (data << cs);
}
// Pull bits from unstuffer (as specified in unstuf_set_count() )
static int
unstuf_pull_bits(struct can2040_bitunstuffer *bu)
@ -504,10 +508,10 @@ unstuf_pull_bits(struct can2040_bitunstuffer *bu)
}
bu->count_stuff = cs = cs - 1;
if (rm_bits & (1 << (cs + 1))) {
// High bit of try_cnt a stuff bit
// High bit is a stuff bit
if (unlikely(rm_bits & (1 << cs))) {
// Six consecutive bits - a bitstuff error
if ((sb >> cs) & 1)
if (sb & (1 << cs))
return -1;
return -2;
}
@ -524,6 +528,13 @@ unstuf_pull_bits(struct can2040_bitunstuffer *bu)
}
}
// Return most recent raw (still stuffed) bits
static uint32_t
unstuf_get_raw(struct can2040_bitunstuffer *bu)
{
return bu->stuffed_bits >> bu->count_stuff;
}
/****************************************************************
* Bit stuffing
@ -630,33 +641,30 @@ tx_qpos(struct can2040 *cd, uint32_t pos)
}
// Queue the next message for transmission in the PIO
static void
static uint32_t
tx_schedule_transmit(struct can2040 *cd)
{
if (cd->tx_state == TS_QUEUED && !pio_tx_did_conflict(cd))
if (cd->tx_state == TS_QUEUED && !pio_tx_did_fail(cd))
// Already queued or actively transmitting
return;
return 0;
if (cd->tx_push_pos == cd->tx_pull_pos) {
// No new messages to transmit
cd->tx_state = TS_IDLE;
return;
pio_signal_clear_txpending(cd);
return SI_TXPENDING;
}
cd->tx_state = TS_QUEUED;
struct can2040_transmit *qt = &cd->tx_queue[tx_qpos(cd, cd->tx_pull_pos)];
pio_tx_send(cd, qt->stuffed_data, qt->stuffed_words);
return 0;
}
// Setup PIO state for ack injection
static int
static void
tx_inject_ack(struct can2040 *cd, uint32_t match_key)
{
if (cd->tx_state == TS_QUEUED && !pio_tx_did_conflict(cd)
&& pio_rx_fifo_level(cd) > 1)
// Rx state is behind - acking wont succeed and may halt active tx
return -1;
cd->tx_state = TS_ACKING_RX;
pio_tx_inject_ack(cd, match_key);
return 0;
}
// Check if the current parsed message is feedback from current transmit
@ -683,7 +691,11 @@ tx_check_local_message(struct can2040 *cd)
// Report state flags (stored in cd->report_state)
enum {
RS_IDLE = 0, RS_IS_TX = 1, RS_IN_MSG = 2, RS_AWAIT_EOF = 4,
RS_NEED_EOF_FLAG = 1<<2,
// States
RS_IDLE = 0, RS_NEED_RX_ACK = 1, RS_NEED_TX_ACK = 2,
RS_NEED_RX_EOF = RS_NEED_RX_ACK | RS_NEED_EOF_FLAG,
RS_NEED_TX_EOF = RS_NEED_TX_ACK | RS_NEED_EOF_FLAG,
};
// Report error to calling code (via callback interface)
@ -713,13 +725,10 @@ report_callback_tx_msg(struct can2040 *cd)
static void
report_handle_eof(struct can2040 *cd)
{
if (cd->report_state == RS_IDLE)
// Message already reported or an unexpected EOF
return;
if (cd->report_state & RS_AWAIT_EOF) {
if (cd->report_state & RS_NEED_EOF_FLAG) { // RS_NEED_xX_EOF
// Successfully processed a new message - report to calling code
pio_sync_normal_start_signal(cd);
if (cd->report_state & RS_IS_TX)
if (cd->report_state == RS_NEED_TX_EOF)
report_callback_tx_msg(cd);
else
report_callback_rx_msg(cd);
@ -728,76 +737,68 @@ report_handle_eof(struct can2040 *cd)
pio_match_clear(cd);
}
// Check if in an rx ack is pending
// Check if in an rx message is being processed
static int
report_is_acking_rx(struct can2040 *cd)
report_is_rx_eof_pending(struct can2040 *cd)
{
return cd->report_state == (RS_IN_MSG | RS_AWAIT_EOF);
return cd->report_state == RS_NEED_RX_EOF;
}
// Parser found a new message start
static void
report_note_message_start(struct can2040 *cd)
{
pio_irq_set_maytx(cd);
pio_irq_set(cd, SI_MAYTX);
}
// Setup for ack injection (if receiving) or ack confirmation (if transmit)
static void
report_note_crc_start(struct can2040 *cd)
{
uint32_t cs = cd->unstuf.count_stuff;
uint32_t crcstart_bitpos = cd->raw_bit_count - cs - 1;
uint32_t last = ((cd->unstuf.stuffed_bits >> cs) << 15) | cd->parse_crc;
uint32_t crc_bitcount = bitstuff(&last, 15 + 1) - 1;
uint32_t crcend_bitpos = crcstart_bitpos + crc_bitcount;
int ret = tx_check_local_message(cd);
if (ret) {
// This is a self transmit - setup tx eof "matched" signal
cd->report_state = RS_IN_MSG | RS_IS_TX;
last = (last << 10) | 0x02ff;
pio_match_check(cd, pio_match_calc_key(last, crcend_bitpos + 10));
cd->report_state = RS_NEED_TX_ACK;
uint32_t bits = (cd->parse_crc_bits << 9) | 0x0ff;
pio_match_check(cd, pio_match_calc_key(bits, cd->parse_crc_pos + 9));
return;
}
// Inject ack
cd->report_state = RS_IN_MSG;
last = (last << 1) | 0x01;
ret = tx_inject_ack(cd, pio_match_calc_key(last, crcend_bitpos + 1));
if (ret)
// Ack couldn't be scheduled (due to lagged parsing state)
return;
pio_irq_set_maytx_ackdone(cd);
// Setup for future rx eof "matched" signal
last = (last << 8) | 0x7f;
cd->report_eof_key = pio_match_calc_key(last, crcend_bitpos + 9);
// Setup for ack inject (after rx fifos fully drained)
cd->report_state = RS_NEED_RX_ACK;
pio_signal_set_txpending(cd);
pio_irq_set(cd, SI_MAYTX | SI_TXPENDING);
}
// Parser successfully found matching crc
static void
report_note_crc_success(struct can2040 *cd)
{
if (cd->report_state == (RS_IN_MSG | RS_IS_TX))
if (cd->report_state == RS_NEED_TX_ACK)
// Enable "matched" irq for fast back-to-back transmit scheduling
pio_irq_set_maytx_matched(cd);
pio_irq_set(cd, SI_MAYTX | SI_MATCHED);
}
// Parser found successful ack
static void
report_note_ack_success(struct can2040 *cd)
{
if (!(cd->report_state & RS_IN_MSG))
// Got rx "ackdone" and "matched" signals already
if (cd->report_state == RS_IDLE)
// Got "matched" signal already
return;
cd->report_state |= RS_AWAIT_EOF;
// Transition RS_NEED_xX_ACK to RS_NEED_xX_EOF
cd->report_state |= RS_NEED_EOF_FLAG;
}
// Parser found successful EOF
static void
report_note_eof_success(struct can2040 *cd)
{
if (cd->report_state == RS_IDLE)
// Got "matched" signal already
return;
report_handle_eof(cd);
pio_irq_set(cd, SI_TXPENDING);
}
// Parser found unexpected data on input
@ -809,35 +810,34 @@ report_note_parse_error(struct can2040 *cd)
pio_match_clear(cd);
}
pio_sync_slow_start_signal(cd);
pio_irq_set_maytx(cd);
pio_irq_set(cd, SI_MAYTX | SI_TXPENDING);
}
// Received PIO rx "ackdone" irq
static void
report_line_ackdone(struct can2040 *cd)
{
if (!(cd->report_state & RS_IN_MSG)) {
// Parser already processed ack and eof bits
pio_irq_set_maytx(cd);
return;
}
// Setup "matched" irq for fast rx callbacks
pio_match_check(cd, cd->report_eof_key);
pio_irq_set_maytx_matched(cd);
uint32_t bits = (cd->parse_crc_bits << 8) | 0x7f;
pio_match_check(cd, pio_match_calc_key(bits, cd->parse_crc_pos + 8));
// Schedule next transmit (so it is ready for next frame line arbitration)
tx_schedule_transmit(cd);
uint32_t check_txpending = tx_schedule_transmit(cd);
pio_irq_set(cd, SI_MAYTX | SI_MATCHED | check_txpending);
}
// Received PIO "matched" irq
static void
report_line_matched(struct can2040 *cd)
{
// Implement fast rx callback and/or fast back-to-back tx scheduling
if (cd->report_state & RS_IN_MSG)
cd->report_state |= RS_AWAIT_EOF;
report_handle_eof(cd);
pio_irq_set_none(cd);
tx_schedule_transmit(cd);
// A match event indicates an ack and eof are present
if (cd->report_state != RS_IDLE) {
// Transition RS_NEED_xX_ACK to RS_NEED_xX_EOF (if not already there)
cd->report_state |= RS_NEED_EOF_FLAG;
report_handle_eof(cd);
}
// Implement fast back-to-back tx scheduling (if applicable)
uint32_t check_txpending = tx_schedule_transmit(cd);
pio_irq_set(cd, check_txpending);
}
// Received 10+ passive bits on the line (between 10 and 17 bits)
@ -845,10 +845,28 @@ static void
report_line_maytx(struct can2040 *cd)
{
// Line is idle - may be unexpected EOF, missed ack injection,
// missed "matched" signal, or can2040_transmit() kick.
report_handle_eof(cd);
pio_irq_set_none(cd);
tx_schedule_transmit(cd);
// or missed "matched" signal.
if (cd->report_state != RS_IDLE)
report_handle_eof(cd);
uint32_t check_txpending = tx_schedule_transmit(cd);
pio_irq_set(cd, check_txpending);
}
// Schedule a transmit
static void
report_line_txpending(struct can2040 *cd)
{
if (cd->report_state == RS_NEED_RX_ACK) {
// Ack inject request from report_note_crc_start()
uint32_t mk = pio_match_calc_key(cd->parse_crc_bits, cd->parse_crc_pos);
tx_inject_ack(cd, mk);
pio_irq_set(cd, SI_MAYTX | SI_ACKDONE);
return;
}
// Tx request from can2040_transmit(), report_note_eof_success(),
// or report_note_parse_error().
uint32_t check_txpending = tx_schedule_transmit(cd);
pio_irq_set(cd, (pio_irq_get(cd) & ~SI_TXPENDING) | check_txpending);
}
@ -897,18 +915,19 @@ data_state_line_error(struct can2040 *cd)
static void
data_state_line_passive(struct can2040 *cd)
{
if (cd->parse_state != MS_DISCARD) {
if (cd->parse_state != MS_DISCARD && cd->parse_state != MS_START) {
// Bitstuff error
data_state_go_discard(cd);
return;
}
uint32_t stuffed_bits = cd->unstuf.stuffed_bits >> cd->unstuf.count_stuff;
uint32_t stuffed_bits = unstuf_get_raw(&cd->unstuf);
uint32_t dom_bits = ~stuffed_bits;
if (!dom_bits) {
// Counter overflow in "sync" state machine - reset it
pio_sync_setup(cd);
cd->unstuf.stuffed_bits = 0;
cd->raw_bit_count = cd->unstuf.count_stuff = 0;
pio_sm_setup(cd);
data_state_go_discard(cd);
return;
}
@ -927,6 +946,14 @@ static void
data_state_go_crc(struct can2040 *cd)
{
cd->parse_crc &= 0x7fff;
// Calculate raw stuffed bits after crc and crc delimiter
uint32_t crcstart_bitpos = cd->raw_bit_count - cd->unstuf.count_stuff - 1;
uint32_t crc_bits = (unstuf_get_raw(&cd->unstuf) << 15) | cd->parse_crc;
uint32_t crc_bitcount = bitstuff(&crc_bits, 15 + 1) - 1;
cd->parse_crc_bits = (crc_bits << 1) | 0x01; // Add crc delimiter
cd->parse_crc_pos = crcstart_bitpos + crc_bitcount + 1;
report_note_crc_start(cd);
data_state_go_next(cd, MS_CRC, 16);
}
@ -1032,6 +1059,10 @@ static void
data_state_update_ack(struct can2040 *cd, uint32_t data)
{
if (data != 0x01) {
// Undo unstuf_clear_state() for correct SOF detection in
// data_state_line_passive()
unstuf_restore_state(&cd->unstuf, (cd->parse_crc_bits << 2) | data);
data_state_go_discard(cd);
return;
}
@ -1055,7 +1086,7 @@ data_state_update_eof0(struct can2040 *cd, uint32_t data)
static void
data_state_update_eof1(struct can2040 *cd, uint32_t data)
{
if (data >= 0x1c || (data >= 0x18 && report_is_acking_rx(cd)))
if (data >= 0x1c || (data >= 0x18 && report_is_rx_eof_pending(cd)))
// Message is considered fully transmitted
report_note_eof_success(cd);
@ -1095,7 +1126,7 @@ data_state_update(struct can2040 *cd, uint32_t data)
* Input processing
****************************************************************/
// Process an incoming byte of data from PIO "rx" state machine
// Process incoming data from PIO "rx" state machine
static void
process_rx(struct can2040 *cd, uint32_t rx_data)
{
@ -1128,7 +1159,7 @@ can2040_pio_irq_handler(struct can2040 *cd)
{
pio_hw_t *pio_hw = cd->pio_hw;
uint32_t ints = pio_hw->ints0;
while (likely(ints & PIO_IRQ0_INTE_SM1_RXNEMPTY_BITS)) {
while (likely(ints & SI_RX_DATA)) {
uint32_t rx_data = pio_hw->rxf[1];
process_rx(cd, rx_data);
ints = pio_hw->ints0;
@ -1136,15 +1167,18 @@ can2040_pio_irq_handler(struct can2040 *cd)
return;
}
if (ints & PIO_IRQ0_INTE_SM3_BITS)
if (ints & SI_ACKDONE)
// Ack of received message completed successfully
report_line_ackdone(cd);
else if (ints & PIO_IRQ0_INTE_SM2_BITS)
else if (ints & SI_MATCHED)
// Transmit message completed successfully
report_line_matched(cd);
else if (ints & PIO_IRQ0_INTE_SM0_BITS)
else if (ints & SI_MAYTX)
// Bus is idle, but not all bits may have been flushed yet
report_line_maytx(cd);
else if (ints & SI_TXPENDING)
// Schedule a transmit
report_line_txpending(cd);
}
@ -1222,7 +1256,7 @@ can2040_transmit(struct can2040 *cd, struct can2040_msg *msg)
writel(&cd->tx_push_pos, tx_push_pos + 1);
// Wakeup if in TS_IDLE state
pio_irq_atomic_set_maytx(cd);
pio_signal_set_txpending(cd);
return 0;
}

View File

@ -63,12 +63,11 @@ struct can2040 {
// Input data state
uint32_t parse_state;
uint32_t parse_crc;
uint32_t parse_crc, parse_crc_bits, parse_crc_pos;
struct can2040_msg parse_msg;
// Reporting
uint32_t report_state;
uint32_t report_eof_key;
// Transmits
uint32_t tx_state;