There are no errata sheets as such.
I hope this is because XMOS chips have no bugs ;)
I've wasted tens of hours on a problem, and came to the conclusion that either:
a) I'm doing something very stupid, or
b) there is a bug in the silicon.
Mainly due to the sparse and irregular documentation, I was unable to determine which one is true.
I hope it is my mistake, but I need help to discover it.
My application is a low-overhead UART receiver, written in assembly.
I thought I figured out how to use ports and events, and wrote the code using
a single port event handler, switching its activity according to the current receiver state.
This worked fine until I made a small addition: a handler for timer (not port timer) events,
which does absolutely nothing apart from scheduling the next timer event in the future.
Suddenly, UART receiver started to get blocked regularly, with quite predictable
average number of characters received before the blocking occurs.
The observed behaviour suggests a possibility that a port timer event
may get lost if it occurs within the same thread cycle as a (non-port) timer event.
This may be true, or it may be completely wrong, I don't know.
I'm attaching the complete assembly file with detailed description of the code and of the problem.
Code: Select all
// .file "UART.S"
#include <xs1b_user.h>
.globl UART
.globl UART.nstackwords
.globl UARTout
.globl UARTout.nstackwords
.linkset UART.nstackwords, 0
.linkset UARTout.nstackwords, 0
// use of registers
// r0..r3 arguments, return values saved by caller
// r4..r10 scratch saved by subroutine
// r11 scratch saved by caller
// cp constant pointer saved by subroutine
// dp data pointer saved by subroutine
// sp stack pointer saved by subroutine
// lr link register saved by subroutine
// RX record layout:
// 0 RXstate
// 1 portaddress
// 2 porttime
// 3 del10bit
// 4 del15bit
// 5 shiftreg
// 6 datareg
// 7 dataready
// RXstate encoding:
// 0 wait for start bit
// 1..8 sample data bits
// 9 sample stop bit
// 10 wait for high level (initial state)
RXrecord:
.int 10 // RXstate = waithighlevel
.int 0x10a00 // portID = 1I
.int 0 // porttime
.int 868 // del10bit (115200 bps)
.int 1302 // del15bit
.int 0 // shiftreg
.int 0 // datareg
.int 0 // dataready
// int UART (void);
// This non-returning function is called to initialize
// and run indefinitely the UART RX and a timer.
UART:
// initialize RXhandler
ldap r11, RXrecord
or r0, r11, r11 // r0 points to RXrecord
ldw r2, r0[1] // get portID to r2
ldc r3, XS1_SETC_INUSE_ON
setc res[r2], r3 // turn the port on
clrpt res[r2] // disable port timer
ldc r3, 1
setd res[r2], r3 // set comparison value
ldc r3, XS1_SETC_COND_EQ
setc res[r2], r3 // set port condition
or r11, r0, r0
setev res[r2], r11 // set environment vector = pointer to RXrecord
ldap r11, RXhandler
setv res[r2], r11 // set event vector
eeu res[r2] // enable events for this port
// initialize TIMERhandler
getr r0, XS1_RES_TYPE_TIMER
ldc r1, XS1_SETC_COND_NONE
setc res[r0], r1 // set timer condition to NONE
in r1, res[r0] // get current time
ldc r2, 434
add r1, r1, r2 // compute due time
setd res[r0], r1 // set due time
ldc r1, XS1_SETC_COND_AFTER
setc res[r0], r1 // set timer condition to AFTER
or r11, r0, r0
setev res[r0], r11 // set environment vector = timerID
ldap r11, TIMERhandler
setv res[r0], r11 // set event vector
eeu res[r0] // enable events for this timer
waiteu // wait for event
// "RXhandler" handles all the UART port events:
// * detecting the initial high level,
// * detecting the start bit,
// * sampling 8 data bits and the stop bit.
// See "RXstate encoding" at the beginning of the file.
// RXhandler runs perfectly if TIMER events are NOT enabled.
// It hangs on indefinitely if TIMER events ARE enabled,
// apparently because a port timer event was missed
// (and the subsequent one was NOT scheduled)
// when it occured in the same thread cycle as the TIMER event.
// This can be demonstrated by commenting in and out
// the timer "eeu" instruction above.
// The probability of a received character to block the RXhandler
// is apparently equal to 1 / (TIMER period in REF cycles), i.e.
// on average the RXhandler will run for "TIMER period" characters,
// before getting stuck.
// This suggests a possibility that a port timer event gets lost
// when it occurs in the same thread cycle as the TIMER event.
// RXhandler is usually stuck in the state "1", i.e. waiting
// for the port timer event in the middle of the first data bit.
// With decreasing probability, it was observed stuck in states "2" and "3",
// waiting for the middle of the second and third data bits.
// It was never observed stuck in states "0" or "10", i.e.
// waiting for a specific level on the input pin.
// Tested on XC1 board with a chip marked "XS1-G4A-FB512-C4 0835 ES N65285.00"
// I think this is the Rev. B of the architecture, and Rev. A of the silicon.
RXhandler:
get r11, ed // get environment vector = pointer to RXrecord
ldw r0, r11[1] // get portID
getts r1, res[r0] // get timestamp
in r2, res[r0] // get port data
ldw r3, r11[0] // get RXstate
bf r3, startbitfound
eq r4, r3, 9
bt r4, checkstopbit
eq r4, r3, 10
bt r4, waitstartbit // high level found
// RXstate in [1..8], sample a data bit
ldw r4, r11[5] // get shiftreg
shr r4, r4, 1
shl r2, r2, 7
or r4, r4, r2 // insert received bit
stw r4, r11[5] // save shiftreg
// now prepare to sample the next bit (data bit or stop bit)
add r3, r3, 1
stw r3, r11[0] // advance RXstate
ldw r1, r11[2] // get porttime
ldw r4, r11[3] // get 1.0 bits delay
add r1, r1, r4
stw r1, r11[2] // save porttime
setpt res[r0], r1 // set port time
waiteu
startbitfound:
add r3, r3, 1
stw r3, r11[0] // advance RXstate
ldc r3, 0
stw r3, r11[5] // clear shiftreg
ldc r4, XS1_SETC_COND_NONE
setc res[r0], r4 // set port condition
ldw r4, r11[4] // get 1.5 bits delay
add r1, r1, r4
stw r1, r11[2] // save porttime
setpt res[r0], r1 // set port time
waiteu
checkstopbit:
eq r4, r2, 1
bf r4, waithighlevel
ldw r4, r11[5] // get shiftreg
stw r4, r11[6] // save to datareg
ldc r4, 1
stw r4, r11[7] // set dataready
waitstartbit:
ldc r4, 0
stw r4, r11[0] // RXstate := wait for start bit
clrpt res[r0] // disable port timer
ldc r4, 0
setd res[r0], r4 // set comparison value
ldc r4, XS1_SETC_COND_EQ
setc res[r0], r4 // set port condition
waiteu
waithighlevel:
ldc r4, 10
stw r4, r11[0] // RXstate := waithighlevel
clrpt res[r0] // disable port timer
ldc r4, 1
setd res[r0], r4 // set comparison value
ldc r4, XS1_SETC_COND_EQ
setc res[r0], r4 // set port condition
waiteu
// "TIMERhandler" just reschedules the timer and waits
TIMERhandler:
get r11, ed // get environment vector = timerID
getd r9, res[r11] // load previous due time
ldc r10, 434 // TIMER period = 434 REF cycles
add r9, r9, r10
setd res[r11], r9 // set new due time
waiteu
// int UARTout (int x);
// read characters received by RXhandler
// from another thread
UARTout:
ldap r11, RXrecord
ldw r0, r11[7] // check dataready
bf r0, UARToutnotready
ldw r0, r11[6] // return data
ldc r2, 0
stw r2, r11[7] // clear dataready
UARToutexit:
retsp 0
UARToutnotready:
ldc r0, 1
neg r0, r0 // return -1 if not ready
bu UARToutexit