Is there an errata sheet available for XS1 chips on my XC1 boards?
Chips are marked XS1-G4A-FB512-C4 0835 ES N65285.00.
Board serial numbers are 77050180 and 77050188.
I'm having some problems with port and timer events in assembly,
looks like some events are missed. I'm not sure about it
and would like to check the relevant errata, if there are any.
Errata Sheet for XS1-G4A-FB512-C4 0835 ES N65285.00
-
- Member
- Posts: 12
- Joined: Sat Jul 03, 2010 1:46 pm
-
- XCore Addict
- Posts: 165
- Joined: Wed Feb 10, 2010 2:32 pm
There are no errata sheets as such.
I've not heard of any problems with events being missed. Perhaps you could post more details of what you're seeing.
I've not heard of any problems with events being missed. Perhaps you could post more details of what you're seeing.
-
- Member
- Posts: 12
- Joined: Sat Jul 03, 2010 1:46 pm
I hope this is because XMOS chips have no bugs ;)There are no errata sheets as such.
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
-
- XCore Expert
- Posts: 844
- Joined: Sun Jul 11, 2010 1:31 am
The events on the port happen with a period an exact multiple of the period on your timer.
This means that as soon as you have an event on both the timer and the port happening
at the same time, from then on there will *always* be an event on the timer when there
is an event on the port. It is unspecified which event will be serviced when there are multiple
at the same time; it looks like your timer is always the lucky one.
Try changing the timer period to 433 or so?
Segher
This means that as soon as you have an event on both the timer and the port happening
at the same time, from then on there will *always* be an event on the timer when there
is an event on the port. It is unspecified which event will be serviced when there are multiple
at the same time; it looks like your timer is always the lucky one.
Try changing the timer period to 433 or so?
Segher
-
- XCore Expert
- Posts: 844
- Joined: Sun Jul 11, 2010 1:31 am
Thinking about it a bit more, this of course doesn't explain it: the port handler
should run immediately after the timer's "waiteu" insn. Try tracing it to see
what's going on (in the simulator)?
Segher
should run immediately after the timer's "waiteu" insn. Try tracing it to see
what's going on (in the simulator)?
Segher
-
- Member
- Posts: 12
- Joined: Sat Jul 03, 2010 1:46 pm
Thank you for looking into code.segher wrote:The events on the port happen with a period an exact multiple of the period on your timer.
This means that as soon as you have an event on both the timer and the port happening
at the same time, from then on there will *always* be an event on the timer when there
is an event on the port. It is unspecified which event will be serviced when there are multiple
at the same time; it looks like your timer is always the lucky one.
Try changing the timer period to 433 or so?
Segher
The start of a character is more or less random in time,
as it depends on typing speed and transmitter clock,
which is not related to XS1 clock. Of course, synchronization
may still be achieved for the duration of a single character.
Changing the timer period to 433 didn't help.
In fact, it made the receiver blocking much faster than before,
on average after a few dozen characters received.
Perhaps ~8 times faster?
The crucial question here is:
if two events happen simultaneously,
and one of them gets serviced immediately,
is the other one lost forever?
With conventional processor the answer is NO, the other one is not lost,
and can be serviced as soon as servicing the first one is done.
But what is the "normal" behaviour of XS1???
The literature is obscure on this question.
-
- XCore Addict
- Posts: 165
- Joined: Wed Feb 10, 2010 2:32 pm
No the unserviced event is not lost forever. When events are re-enabled it will be taken.va3ttn wrote:The crucial question here is:
if two events happen simultaneously,
and one of them gets serviced immediately,
is the other one lost forever?
With conventional processor the answer is NO, the other one is not lost,
and can be serviced as soon as servicing the first one is done.
But what is the "normal" behaviour of XS1???
The literature is obscure on this question.
Longer answer:
One or more resources may be setup to create an event when a certain condition occurs (e.g. past a specific time on a timer, or when a port has more data to input). Events are then globally enabled and usually the thread pauses (waiting for the first event). When the first event occurs it is serviced. Unlike an interrupt, the code servicing an event does not return: the event is more like a branch than a call.
Within XC a 'select' statement is used to: setup the required resource conditions; enable events on the resources; and then enable events globally. Only one of the events created by the select is serviced. What quite often happens is that the select statement is within a loop of some kind and when the select statement is re-executed, if the unserviced resource's condition is true at this time then it will be serviced immediately. (If more than one resource wants to create an event then only one will occur again).
So if you're working in assembler, then you need to re-enable events globally and the second event should occur.
Can I ask why you're writing in assembler? Have you tried writing the code in XC? XC executes pretty fast and has been developed in tandem with the XCore so it is very quick to write and problems like this are often avoided.
-
- Member
- Posts: 12
- Joined: Sat Jul 03, 2010 1:46 pm
Great, so my basic assumption didn't turn wrong :PNo the unserviced event is not lost forever. When events are re-enabled it will be taken.
This is exactly what I have done.One or more resources may be setup to create an event when a certain condition occurs (e.g. past a specific time on a timer, or when a port has more data to input). Events are then globally enabled and usually the thread pauses (waiting for the first event). When the first event occurs it is serviced.
In my code, servicing a TIMER event ends with WAITEU,
which should result in accepting the outstanding timed input event.
But somehow, the timed input gets lost forever if the TIMER is enabled simultaneously.
Servicing an event ends with WAITEU, so the second event should occur, but it doesn't.So if you're working in assembler, then you need to re-enable events globally and the second event should occur.
Is it possible that I have done something that nullifies the timed input event
when it occurs simultaneously with a TIMER event?
I wanted a single thread to handle a number of UART receivers and transmitters.Can I ask why you're writing in assembler? Have you tried writing the code in XC? XC executes pretty fast and has been developed in tandem with the XCore so it is very quick to write and problems like this are often avoided.
The resulting SELECT had a large number of enabling sequences, and all of them are executed
whenever a SINGLE event is serviced. This made it impossible to meet the timing requirements.
Moreover, SELECT emits CLRE (at the top of the loop), and I wasn't sure I could quite comprehend its effects.
-
- Member
- Posts: 12
- Joined: Sat Jul 03, 2010 1:46 pm
I remember the transputer having some special use for the memory location pointed to by Wptr,
and some other locations "at small negative offsets from Wptr".
With XS1, is it possible to induce an interference between timed input events
and TIMER events via some similar "special use" of memory locations?
and some other locations "at small negative offsets from Wptr".
With XS1, is it possible to induce an interference between timed input events
and TIMER events via some similar "special use" of memory locations?
-
- Respected Member
- Posts: 318
- Joined: Tue Dec 15, 2009 12:46 am
The code looks fine to me at a first glance, so I agree this is puzzling.
Could you clarify exactly what you mean by gets blocked? Do you stop receiving characters or do you just miss some? Do the TIMERhandler events continue to fire? I would suggest using xrun xgdb to read the register state of the thread and also read the data in the RXRecord after it becomes blocked to see if this reveals anything. The pc of the thread and the current RXState would be of particular interest.va3ttn wrote:Suddenly, UART receiver started to get blocked regularly, with quite predictable
average number of characters received before the blocking occurs.