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:

Listing 20 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:

Listing 21 main.c#
#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");
}