Errata Sheet for XS1-G4A-FB512-C4 0835 ES N65285.00
-
- Respected Member
- Posts: 318
- Joined: Tue Dec 15, 2009 12:46 am
Edit: We are still investigating and checking other solutions.
-
- XCore Expert
- Posts: 844
- Joined: Sun Jul 11, 2010 1:31 am
Would it work to run the timer event on another thread, or will that run into
the same problem?
Segher
the same problem?
Segher
-
- Respected Member
- Posts: 318
- Joined: Tue Dec 15, 2009 12:46 am
A second possibility is to use a combination of a timer and an unbuffered port. Since the UART RX port is clocked off the undivided reference clock the number of timer ticks before a timed input on the port becomes ready is equal to the number of port counter increments before the port time is met. You can use this to implement the UART as follows.
When you want to sample the value of the port at a time in the future use the setpt instruction with the desired time as before. Instead of enabling events on the port, input the current time on a timer and configure the timer to event after uart bit time timer ticks have elapsed (or 1.5 bit times if you have just had the start bit). Finally enable events on thread.
When the timer event is taken the port time is guaranteed to have been met. The port will have sampled the pins at this time and will hold this value until it is read. An in instruction can be used to read the value. Once you have read the value you can configure the time and the port for the bit in the same way and enable events on the thread again.
I've written a XC and a modified assembly version of the UART to illustrate this idea.
When you want to sample the value of the port at a time in the future use the setpt instruction with the desired time as before. Instead of enabling events on the port, input the current time on a timer and configure the timer to event after uart bit time timer ticks have elapsed (or 1.5 bit times if you have just had the start bit). Finally enable events on thread.
When the timer event is taken the port time is guaranteed to have been met. The port will have sampled the pins at this time and will hold this value until it is read. An in instruction can be used to read the value. Once you have read the value you can configure the time and the port for the bit in the same way and enable events on the thread again.
I've written a XC and a modified assembly version of the UART to illustrate this idea.
Code: Select all
#include "xs1.h"
#define UART_TICKS 868
#define UART_TICKS_1_5 1302
static void setpt(port p, unsigned time)
{
asm("setc res[%0], 1" : : "r"(p)); // XS1_SETC_COND_NONE
asm("setpt res[%0], %1" : : "r"(p), "r"(time));
}
static unsigned portin(port p)
{
unsigned value;
asm("in %0, res[%1]" : "=r"(value) : "r"(p));
return value;
}
unsigned uart_rx_count;
unsigned uart_rx_val;
void uart_rx(port p) {
timer t;
timer periodicTimer;
unsigned periodicTime;
unsigned time;
unsigned timerTime = 0;
unsigned portTime = 0;
unsigned portValue = 0;
unsigned byte;
unsigned bit = 0;
int timedInput = 0;
periodicTimer :> periodicTime;
periodicTime += 434;
while (1) {
select {
case periodicTimer when timerafter(periodicTime) :> void:
periodicTime += 434;
break;
case !timedInput => p when pinseq(portValue) :> void @ portTime:
if (portValue) {
// Wait for start condition
portValue = 0;
} else {
// Wait for first bit.
t :> timerTime;
timerTime += UART_TICKS_1_5;
portTime += UART_TICKS_1_5;
setpt(p, portTime);
timedInput = 1;
bit = 0;
byte = 0;
}
break;
case timedInput => t when timerafter(timerTime) :> void:
portValue = portin(p);
if (bit == 8) {
unsigned tmp;
// Read stop bit
timedInput = 0;
// If bit is 0 wait for high, otherwise wait for start condition.
if (portValue) {
portValue = 0;
uart_rx_count++;
uart_rx_val = byte;
} else {
portValue = 1;
}
} else {
byte |= portValue << bit;
bit++;
timerTime += UART_TICKS;
portTime += UART_TICKS;
setpt(p, portTime);
}
break;
}
}
}
Code: Select all
#include <xs1.h>
.section .dp.data, "awd", @progbits
.align 4
RXrecord:
.int 0 // bit number
.int XS1_PORT_1B // portID
.int 0 // timerID
.int 0 // port time
.int 868 // del10bit (115200 bps)
.int 1302 // del15bit
.int 0 // shiftreg
.int 0 // datareg
.int 0 // dataready
#define BIT_OFFSET 0
#define PORTID_OFFSET 1
#define TIMERID_OFFSET 2
#define PORTTIME_OFFSET 3
#define BITTIME_OFFSET 4
#define BITTIME1_5_OFFSET 5
#define SHIFT_OFFSET 6
#define DATA_OFFSET 7
#define DATAREADY_OFFSET 8
.text
.globl UART
.globl UART.nstackwords
.linkset UART.nstackwords, 0
.align 2
UART:
ldaw r0, dp[RXrecord]
// Allocate and initialise periodic timer
getr r1, XS1_RES_TYPE_TIMER
setc res[r1], XS1_SETC_COND_AFTER
ldap r11, TimerHandler
setv res[r1], r11
in r2, res[r1]
ldc r3, 434
add r2, r2, r3
setd res[r1], r2
eeu res[r1]
// Allocate and initialise rx timer
getr r1, XS1_RES_TYPE_TIMER
setc res[r1], XS1_SETC_COND_AFTER
stw r1, r0[TIMERID_OFFSET]
ldap r11, RXTimerHandler
setv res[r1], r11
mov r11, r0
setev res[r1], r11
// Initialise port
ldw r1, r0[PORTID_OFFSET]
setc res[r1], XS1_SETC_INUSE_ON
ldap r11, RXHandler
setv res[r1], r11
mov r11, r0
setev res[r1], r11
// setup port to event when high
setc res[r1], XS1_SETC_COND_EQ
ldc r2, 1
setd res[r1], r2
eeu res[r1]
waiteu
RXHandler:
get r11, ed // get environment vector = RXrecord
ldw r0, r11[PORTID_OFFSET]
getts r1, res[r0]
in r2, res[r0]
bt r2, waitstartbit
startbitfound:
ldw r2, r11[TIMERID_OFFSET]
in r3, res[r2]
ldw r4, r11[BITTIME1_5_OFFSET]
edu res[r0] // disable port events
setc res[r0], XS1_SETC_COND_NONE // unset port condition
add r1, r1, r4
setpt res[r0], r1 // set port time
stw r1, r11[PORTTIME_OFFSET]
add r3, r3, r4
setd res[r2], r3 // set timer time
eeu res[r2] // enable timer events
ldc r1, 0
stw r1, r11[BIT_OFFSET] // store bit = 0
stw r1, r11[SHIFT_OFFSET] // clear shift register
waiteu
waitstartbit:
ldc r2, 0
setd res[r0], r2
waiteu
RXTimerHandler:
get r11, ed // get environment vector = RXrecord
ldw r0, r11[TIMERID_OFFSET]
in r1, res[r0]
ldw r1, r11[PORTID_OFFSET]
ldw r2, r11[BIT_OFFSET]
eq r3, r2, 8
bt r3, checkstopbit
ldw r3, r11[SHIFT_OFFSET]
inshr r3, res[r1]
stw r3, r11[SHIFT_OFFSET]
add r2, r2, 1
stw r2, r11[BIT_OFFSET]
ldw r2, r11[BITTIME_OFFSET]
getd r3, res[r0]
add r3, r3, r2
setd res[r0], r3 // set timer time
ldw r3, r11[PORTTIME_OFFSET]
add r3, r3, r2
setpt res[r1], r3 // set port time
stw r3, r11[PORTTIME_OFFSET]
waiteu
checkstopbit:
edu res[r0] // disable port events
in r2, res[r1]
setc res[r1], XS1_SETC_COND_EQ
eq r2, r2, 0
setd res[r1], r2 // if 1 was sampled wait for 0, if 0 was sampled wait for 1
eeu res[r1] // enable port events
waitet r2 // don't store data if stop bit wasn't 1
ldw r2, r11[SHIFT_OFFSET]
shr r2, r2, 24 // shift data to least significant bits
stw r2, r11[DATA_OFFSET] // store data
ldc r2, 1
stw r2, r11[DATAREADY_OFFSET] // set dataready
waiteu
TimerHandler:
get r11, ed // get environment vector = timerID
in r0, res[r11]
getd r0, 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
-
- Respected Member
- Posts: 318
- Joined: Tue Dec 15, 2009 12:46 am
Yes, this would be OK as well. It will work correctly so long as you are guaranteed not to get another event at the same time as the event on the port.segher wrote:Would it work to run the timer event on another thread, or will that run into
the same problem?
Segher
-
- Member
- Posts: 12
- Joined: Sat Jul 03, 2010 1:46 pm
True, XC has no equivalent construct.dave wrote:We have checked that XC programs are not affected by this behavior - the instruction sequences compiled can not give rise to it.
That's why I had to use assembly to get to the full potential of this wonderful technology.
Unexpected, and gladly accepted :-)dave wrote: Thank you for finding this issue and pointing it out to us.
Can I offer you a reward - a free XC-1A or equivalent?
An equivalent in the form of XK-1 with XTAG-2 fits my immediate needs perfectly.
I sent contact details in a private message. Thank you!
Last edited by va3ttn on Thu Jul 29, 2010 10:43 am, edited 1 time in total.
-
- Member
- Posts: 12
- Joined: Sat Jul 03, 2010 1:46 pm
Let me first thank all of you nice people from XMOS for considering my problem so carefully and promptly.richard wrote:As Dave says you have encountered a limitation of the port hardware...
In the actual application, a single thread was to implement 8 UART receivers,
10 UART transmitters plus some additional gadgets.
Other threads (on Core 2) have their own duties, such as generating two RGB video signals (640 x 400, 8 colors each), rendering, etc.
But, happily enough, a few spare threads still remained. Eight threads (per core) can really go a long way :-)
So, I could reproduce the problem without a timer, just by using two or more UART receivers simultaneously.
What is the exact extent of this limitation?richard wrote:...The issue concerns waiting for events on a unbuffered port configured to become ready at a port time with no condition. If the port becomes ready at the same time as another resource and the event for the other resource is taken then the port will not generate another event until its configuration is changed.
Does it also apply to ports in interrupt mode?
It would be very nice if this limitation could be removed in future versions of silicon.
This may be unimportant for XC, but it would certainly enable more effective use
of the available resources in some applications.
Thank you very much for the suggestion, it worked!richard wrote:One possibility is to instead configure the port as a buffered port...
The timing uncertainty wasn't small with all the burden I've placed on the poor thread.
But it was easy to rearrange things so that only 8 UART receivers remained, with the rest placed on a spare thread.
At 115200 bps the timing uncertainty now amounted to about 24 % of the bit period,
so I've shifted the sampling point to distribute this uncertainty symmetrically
about bit center. The resulting +/- 12 % seem to have no ill effect at all.
This solution has a great virtue of preserving the precise input sampling.richard wrote:A second possibility is to use a combination of a timer and an unbuffered port.
But I feared that for 8 concurrent receivers the necessary time bookkeeping may turn too complex.
So, I stayed with the first solution, but will have this in mind.
Thank you very much for bringing my project back to tracks!
-
- Respected Member
- Posts: 318
- Joined: Tue Dec 15, 2009 12:46 am
Yes, the same applies to interrupts.va3ttn wrote:Does it also apply to ports in interrupt mode?