XC to C cheat sheet#
This cheat sheet is intended to act as a quick reference for experienced XC programmers wanting to migrate to using C as per Programming an XCORE tile with C and lib_xcore.
Parallel tasks & channels#
XC |
C |
---|---|
void task1(chanend c) { ... }
void task2(chanend c, int count) { ... }
void task3(void) { ... }
int main(void)
{
int count = 5;
chan c;
par {
task1(c);
task2(c, count);
task3();
}
}
|
#include <xcore/channel.h>
#include <xcore/parallel.h>
DECLARE_JOB(task1, (chanend_t));
void task1(chanend_t c) { ... }
DECLARE_JOB(task2, (chanend_t, int));
void task2(chanend_t c, int count) { ... }
DECLARE_JOB(task3, (void));
void task3(void) { ... }
int main(void)
{
int count = 5;
channel_t c = chan_alloc();
PAR_JOBS(
PJOB(task1, (c.end_a)),
PJOB(task2, (c.end_b, count)),
PJOB(task3, ())
);
chan_free(c);
}
|
int a = 5;
c <: a;
c :> a;
|
int a = 5;
chan_out_word(c, a);
a = chan_in_word(c);
|
unsigned char a = 5;
c <: a;
c :> a;
|
unsigned char a = 5;
chan_out_byte(c, a);
a = chan_in_byte(c);
|
NA |
uint32_t words[5] = {1,2,3,4,5};
chan_out_buf_word(c, words, 5);
chan_in_buf_word(c, words, 5);
|
NA |
unsigned char bytes[4] = {1,2,3,4};
chan_out_buf_byte(c, bytes, 4);
chan_in_buf_byte(c, bytes, 4);
|
streaming chan c;
int a = 5;
c <: a;
c :> a;
|
#include <xcore/channel_streaming.h>
streaming_channel_t c = s_chan_alloc();
// use streaming channel
s_chan_free();
int a = 5;
s_chan_out_word(c, a);
a = s_chan_in_word(c);
|
Ports#
XC |
C |
---|---|
#include <platform.h>
port my_port = XS1_PORT_1J;
void port_user(port p) { ... }
int main(void)
{
port_user(my_port);
}
|
#include <platform.h>
#include <xcore/port.h>
void port_user(port_t p) { ... }
int main(void)
{
port_t my_port = XS1_PORT_1J;
port_enable(my_port);
port_user(my_port);
port_disable(my_port);
}
|
int a;
p :> a;
p <: a;
|
int a;
a = port_in(p);
port_out(p, a);
|
Timers#
XC |
C |
---|---|
int main()
{
[[hwtimer]]
timer t;
unsigned v;
t :> v;
}
|
#include <xcore/hwtimer.h>
int main(void)
{
hwtimer_t t = hwtimer_alloc();
unsigned v;
v = hwtimer_get_time(t);
hwtimer_free(t);
}
|
‘Selecting’ events#
Select Blocks#
XC |
C |
---|---|
#include <stdio.h>
#include <platform.h>
int main(void)
{
[[hwtimer]]
timer t;
unsigned long now;
t :> now;
while (1)
{
select
{
case t when timerafter(now + 10000000) :> now:
printf("Timer handler at time: %lu\n",
now);
break;
default:
puts("Nothing happened...");
break;
}
}
}
|
#include <stdio.h>
#include <xcore/hwtimer.h>
#include <xcore/select.h>
int main(void)
{
hwtimer_t t = hwtimer_alloc();
unsigned long now = hwtimer_get_time(t);
hwtimer_set_trigger_time(t,
now + 10000000);
SELECT_RES(
CASE_THEN(t, timer_handler),
DEFAULT_THEN(default_handler))
{
timer_handler:
now = hwtimer_get_time(t);
hwtimer_change_trigger_time(t,
now + 10000000);
printf("Timer handler at time: %lu\n",
now);
continue;
default_handler:
puts("Nothing happened...");
continue;
}
hwtimer_free(t);
}
|
[[ordered]]
select{ // case statements }
|
SELECT_RES_ORDERED( // case specifiers ){}
|
XC’s [[combine]]
and [[combinable]]
keywords are not available in
C. Combine the functions manually.
Case specifiers#
XC |
C |
---|---|
Guarded case: case guard_flag => t :> now:
|
CASE_GUARD_THEN(t, guard_flag, timer_handler)
timer_handler:
now = hwtimer_get_time(t);
|
Guarded default case: guard_flag => default:
|
DEFAULT_GUARD_THEN(guard_flag, default_handler)
|
Inverted guarded case: case !guard_flag => t :> now:
|
CASE_NGUARD_THEN(t, guard_flag, timer_handler)
timer_handler:
now = hwtimer_get_time(t);
|
Inverted guarded default case: !guard_flag => default:
|
DEFAULT_NGUARD_THEN(guard_flag, default_handler)
|
Event handlers#
Event handlers are denoted by a label which must be immediately followed by a read from the event-generating resource, even if the value is not required.
Event handlers can terminated with the following:
break
Exit implicit select block loop
continue
Continue implicit select block loop; re-initialise as appropriate
With care, and depending on the likely presence or guaranteed absence of a
nested select block, continue
in the outer select block may be replaced
with:
SELECT_CONTINUE_RESET
Continue; force re-initialisation
SELECT_CONTINUE_NO_RESET
Continue; force no re-initialisation
Locks#
XC |
C |
---|---|
NA |
#include <xcore/lock.h>
int task_func(void)
{
lock_t l = lock_alloc();
lock_acquire(l);
// Critical section
lock_release(l);
lock_free(l);
}
|
Locks are not ‘eventable’ and therefore cannot be referenced within a select block case specifier.
Function pointers#
Assist stack size calculation by annotating function pointers:
__attribute__(( fptrgroup("my_functions") ))
void func1_small(void) { char cs[64]; cs[0] = 0; }
__attribute__(( fptrgroup("my_functions") ))
void func2_big(void) { char cs[256]; cs[0] = 0; }
int main(void)
{
__attribute__(( fptrgroup("my_functions") ))
void(*fp)(void);
fp = func1_small; fp();
fp = func2_big; fp();
}
Membership of the correct group is not checked at build-time. Enable runtime checking in the above using the fragment below when the function pointer is declared:
__attribute__(( fptrgroup("my_functions", 1) ))
void(*fp)(void);
Use of the same fragment on the function declaration has no effect.
Targeting multiple tiles#
Deploying applications onto multiple tiles still requires the use of a minimal
multitile.xc
:
#include <platform.h>
typedef chanend chanend_t;
extern "C" {
void main_tile0(chanend_t);
void main_tile1(chanend_t);
}
int main(void)
{
chan c;
par {
on tile[0]: main_tile0(c);
on tile[1]: main_tile1(c);
}
return 0;
}
Avoid all procedural code within multitile.xc
. Instead, place it within
the C code:
#include <stdio.h>
#include <xcore/channel.h>
void main_tile0(chanend_t c)
{
printf("Tile 0 prints first\n");
chan_out_word(c, 1);
}
void main_tile1(chanend_t c)
{
int token = chan_in_word(c);
printf("Tile 1 prints second\n");
}