Errata Sheet for XS1-G4A-FB512-C4 0835 ES N65285.00

Technical discussions around xCORE processors (e.g. xcore-200 & xcore.ai).
va3ttn
Member
Posts: 12
Joined: Sat Jul 03, 2010 1:46 pm

Errata Sheet for XS1-G4A-FB512-C4 0835 ES N65285.00

Post by va3ttn »

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.


User avatar
Woody
XCore Addict
Posts: 165
Joined: Wed Feb 10, 2010 2:32 pm

Post by Woody »

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.
va3ttn
Member
Posts: 12
Joined: Sat Jul 03, 2010 1:46 pm

Post by va3ttn »

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
User avatar
segher
XCore Expert
Posts: 844
Joined: Sun Jul 11, 2010 1:31 am
Contact:

Post by segher »

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
User avatar
segher
XCore Expert
Posts: 844
Joined: Sun Jul 11, 2010 1:31 am
Contact:

Post by segher »

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
va3ttn
Member
Posts: 12
Joined: Sat Jul 03, 2010 1:46 pm

Post by va3ttn »

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
Thank you for looking into code.

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.
User avatar
Woody
XCore Addict
Posts: 165
Joined: Wed Feb 10, 2010 2:32 pm

Post by Woody »

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.
No the unserviced event is not lost forever. When events are re-enabled it will be taken.

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.
va3ttn
Member
Posts: 12
Joined: Sat Jul 03, 2010 1:46 pm

Post by va3ttn »

No the unserviced event is not lost forever. When events are re-enabled it will be taken.
Great, so my basic assumption didn't turn wrong :P
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.
This is exactly what I have done.
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.
So if you're working in assembler, then you need to re-enable events globally and the second event should occur.
Servicing an event ends with WAITEU, so the second event should occur, but it doesn't.

Is it possible that I have done something that nullifies the timed input event
when it occurs simultaneously with a TIMER event?

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.
I wanted a single thread to handle a number of UART receivers and transmitters.
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.
va3ttn
Member
Posts: 12
Joined: Sat Jul 03, 2010 1:46 pm

Post by va3ttn »

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?
richard
Respected Member
Posts: 318
Joined: Tue Dec 15, 2009 12:46 am

Post by richard »

The code looks fine to me at a first glance, so I agree this is puzzling.
va3ttn wrote:Suddenly, UART receiver started to get blocked regularly, with quite predictable
average number of characters received before the blocking occurs.
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.
Post Reply