Today I got interrupt-driven network I/O working.
There's actually a
little more to interrupts than I'd realised. We have two separate bits
of hardware dealing with them: the CAN network controller sets
interrupt signals based on the CAN bus status (data to receive? ready
to transmit?) and the Vectored Interrupt Controller (VIC)
decides how these signals should affect the CPU's execution. I'm using the CAN interrupts to track the bus status
and using the VIC to temporarily disable interrupts when they're inconvenient, i.e. when I'm accessing a data structure that's shared between ISR (Interrupt Service Routine) and non-ISR code. These data structures are FIFO queues based on the ring code that I posted before.
The tricky part was figuring out exactly when the CAN controller sets and clears interrupt status. Looks like it's based on discrete events (e.g. CAN frame successfully sent on the bus) rather than just conditions (CAN transmit buffer is empty).
Here's the code for receiving
data from the CAN bus:
\ Is there a CAN frame ready to receive?
: can-rx? ( -- flag )
CAN-GSR can@ $1 and 0 <>
;
\ Return the number of bytes of data in incoming CAN frame.
: can-dlen ( -- n )
CAN-RFS can@
16 rshift $F and
8 min
;
\ Free CAN Rx hardware buffers
: can-ack-rx ( -- ) $4 ( RRB ) CAN-CMR can! ;
\ Receive a CAN frame (blocking)
: can-rx ( -- dB dA len id )
BEGIN can-rx? UNTIL
CAN-RDB can@ CAN-RDA can@ can-dlen CAN-RID can@
can-ack-rx
;
And here's the interrupt service routine that wakes up when a frame
becomes available on the bus and puts it onto the RX queue for
application-level processing:
\ Read a CAN frame from the controller and put it on the RX queue.
: can-rx-isr ( -- )
can-rx-ring ring-full? IF
can-disable-rx-interrupt
ELSE
can-rx can-rx-ring >ring
THEN
;
Here're the words that non-interrupt code uses to temporarily mask out
CAN interrupts to safely access the RX queue:
$4100000 ( CAN1-RX CAN1-TX ) CONSTANT CAN-interrupts
: mask-can{ ( -- ) CAN-interrupts disable-interrupts ;
: }mask-can ( -- ) CAN-interrupts enable-interrupts ;
And here's the non-interrupt code that reads frames from the queue:
: can-dequeue? ( -- flag )
mask-can{ can-rx-ring ring-empty? not }mask-can
;
: can-dequeue ( -- dB dA len id )
BEGIN can-dequeue? UNTIL
mask-can{
can-rx-ring ring>
}mask-can
can-enable-rx-interrupt
;
How do other people's Forth-based interrupt handlers look? |