Practice

Forklift CAN Arbitration

Two forklift controllers at Halewick Cold Store share one CAN pair. On CAN, a 0 bit is dominant, so a sender that drives 0 wins the wire.

Each sender must read the bus while it writes.

When both controllers began a message together, the one with the higher identifier kept driving after it had lost. Both messages died.

Each collision caused a storm of resends. Forklifts froze beneath flashing amber beacons while a milk lorry waited at Bay 4.

The fix is a front end that sends the arbitration field and listens to every bit. It must go silent as soon as it loses. If it wins, it hands the bus to the block that sends the data.

Your task: build that arbitration front end. Send SOF, the 11-bit identifier, RTR, and any needed stuff bits. Check the bus at each sample point and report whether the attempt won or lost.

Interface

PortDirectionTypeDescription
clk_iin1 bitClock
rst_iin1 bitSynchronous reset (active high)
start_iin1 bitBegin one arbitration attempt
id_iin11-bit vectorMessage identifier
can_rx_iin1 bitBus level seen by the transceiver
can_tx_oout1 bitTransceiver drive level, dominant 0, recessive 1
busy_oout1 bitArbitration attempt is active
won_oout1 bitArbitration won (one-clock pulse)
lost_oout1 bitArbitration lost (one-clock pulse)
handoff_oout1 bitWon field complete (one-clock pulse)

Behavior

  • While idle, can_tx_o is recessive high, and busy_o, won_o, lost_o, and handoff_o are low
  • start_i is sampled only while idle, and id_i is captured on the accepting rising edge
  • The attempt begins on the following rising edge: busy_o rises and the SOF bit begins
  • Changes to id_i after acceptance do not affect the running attempt
  • start_i while busy_o is high is ignored
  • Every transmitted bit lasts exactly four clocks
  • can_tx_o changes only at a bit boundary and holds the same value for all four clocks of that bit
  • can_rx_i is sampled at the third clock of every bit
  • The field contains one dominant SOF bit, the 11 captured identifier bits MSB first, then one recessive RTR bit
  • Bit stuffing starts with SOF and tracks every sent bit
  • After five identical consecutive sent bits, one opposite-level stuff bit is inserted before the next field bit
  • Inserted stuff bits join the run count and are subject to the same arbitration rule as all other bits
  • A recessive transmitted bit loses arbitration if can_rx_i is dominant at its sample point
  • At that sample edge, lost_o pulses for one clock and busy_o falls
  • After a loss, can_tx_o is recessive from the next bit boundary and no more field bits are sent
  • Sampling dominant while transmitting dominant is normal and does not lose arbitration
  • If the complete stuffed field finishes without a loss, won_o pulses at the final bit's sample point
  • handoff_o pulses on the following clock, with busy_o still high and won_o low
  • On the next clock, busy_o falls and can_tx_o is recessive
  • won_o, lost_o, and handoff_o are each high for exactly one clock
  • rst_i high at any rising edge cancels the attempt and returns the unit to idle
  • After reset, can_tx_o is recessive and every other output is low

What the bench checks

  • The testbench checks idle, reset, start_i acceptance, captured id_i, and ignored requests while busy
  • It checks every bit boundary, all four clocks per bit, and the third-clock sample point
  • It compares the transmitted field against SOF, MSB-first identifier, RTR, and required stuff bits
  • It forces dominant bus levels during recessive identifier, RTR, and stuff bits to check immediate loss reporting
  • It checks that dominant transmitted bits never lose when the sampled bus is dominant
  • It checks the exact won_o, handoff_o, and busy_o sequence after a complete field
  • It asserts reset at several bit phases and checks an immediate registered return to idle

Constraints

TIMING: everything is synchronous to clk_i; all outputs are registered. can_tx_o may change only at bit boundaries, and can_rx_i is sampled on each bit's third clock.

SCOPE: this is an arbitration-field front end, not a CAN controller. CRC, ACK, error frames, resynchronization, and the data frame are out of scope. can_tx_o and can_rx_i are the transceiver's logic pins. The wired-AND bus lives in the transceiver and external wiring.

Do not add ports.

Loading editor...

Click Run to execute your code. Output will appear here.