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
clkin1 bitClock
rstin1 bitSynchronous reset (active high)
startin1 bitBegin one arbitration attempt
idin11-bit vectorMessage identifier
can_rxin1 bitBus level seen by the transceiver
can_txout1 bitTransceiver drive level, dominant 0, recessive 1
busyout1 bitArbitration attempt is active
wonout1 bitArbitration won (one-clock pulse)
lostout1 bitArbitration lost (one-clock pulse)
handoffout1 bitWon field complete (one-clock pulse)

Behavior

  • While idle, can_tx is recessive high, and busy, won, lost, and handoff are low
  • start is sampled only while idle, and id is captured on the accepting rising edge
  • The attempt begins on the following rising edge: busy rises and the SOF bit begins
  • Changes to id after acceptance do not affect the running attempt
  • start while busy is high is ignored
  • Every transmitted bit lasts exactly four clocks
  • can_tx changes only at a bit boundary and holds the same value for all four clocks of that bit
  • can_rx 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 is dominant at its sample point
  • At that sample edge, lost pulses for one clock and busy falls
  • After a loss, can_tx 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 pulses at the final bit's sample point
  • handoff pulses on the following clock, with busy still high and won low
  • On the next clock, busy falls and can_tx is recessive
  • won, lost, and handoff are each high for exactly one clock
  • rst high at any rising edge cancels the attempt and returns the unit to idle
  • After reset, can_tx is recessive and every other output is low

What the bench checks

  • The testbench checks idle, reset, start acceptance, captured id, 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, handoff, and busy 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; all outputs are registered. can_tx may change only at bit boundaries, and can_rx 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 and can_rx 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.