Contents
- 1 Fold handling with Collapse-O-Matic plugin
- 2 xC == XC
- 3 The official definition
- 4 xC and Wikipedia
- 5 Background info
- 6 The XMOS glossary of terms
- 7 How does xC compare with C?
- 8 Reserved words
- 9 Messages
- 10 No liveness analysis
- 11 XMOS series of processors
- 12 Hidden xC
- 12.1 timerafter (future)
- 12.2 Reinterpretation
- 12.3 Replicated par and placements
- 12.4 [[combine]] and [[combinable]]
- 12.5 [[distribute]] and [[distributable]]
- 12.6 Any commen term for combinable and distributable?
- 12.7 XMOS libs: synchronous/blocking when [[distributable]] and asynchronous when [[combinable]]
- 12.8 [[distributed(task,n)]]
- 12.9 A nested select can save you a [[guarded]]
- 12.10 Sharing memory
- 12.11 Ordered select combined with default case
- 12.12 Configuration (mapping) language
- 12.13 Parallel usage checks
- 12.14 memcpy or not?
- 12.15 Priority
- 13 Chanend in select
- 14 Select
- 15 Synchronous, or asynchronous with streaming or buffered
- 16 Off-piste xC code examples
- 17 Running xTIMEcomposer functions from command line
- 18 Other
- 18.1 Porting to XMOS / xC
- 18.2 Data types long long, float, double and long double
- 18.3 Inserting run-time checks
- 18.4 Architecture defines
- 18.5 Including interface functions in local .xc file or library
- 18.6 nullable types, null and NULL
- 18.7 Not used parameter not set up in call
- 18.8 xCore instruction set
- 18.9 PWM examples
- 18.10 Chan or interface when critical timing
- 19 XMOS libraries
- 20 FreeRTOS
- 21 Erlang vs xC
- 22 Buffered asynchronous real-time printf
- 23 Communities
- 24 References
This page is in group Technology (plus My XMOS pages) and is a blog note trying to scribble down some info about the XMOS xC language that I haven’t found elsewhere. I love it. But, alas, xC appears as C plus unknown X. I’ll help finding min(X). Also see more xC code examples.
Update 14May2021: Press drawing or C plus lib_xcore black-boxes xC. XTC Tools and lib_xcore
represent a paradigm shift.
For the TIOBE index 016:[10]: «xC programming».
The xTIMEcomposer would build from C and C++ in addition to xC sources. Only the additions for xC are handled here.
Disclaimers: Standard disclaimer (here). Extra disclaimer: take this as face value. This note is about the xTIMEcomposer and xC as seen from a user only. There may be errors or misunderstandings done by me. Or simply ignorance. Please mail me or comment if you find any.
Fold handling with Collapse-O-Matic plugin
I am using Collape-O-Matic (here) on many pages.
Collapse All |
Typical fold
xC == XC
Update 14May2021: In the new XTC Tools and C plus lib_xcore
the designers seem to use «XC» (both capital).
Before that update: XMOS seems to call the language xC with lower-case c [8]. I have tried to remove all traces of «XC» in my blog notes.. There are excellent manuals, see References (below). But I started this note to fill in the holes that I didn’t find any patches for.
The official definition
«To program an XMOS device you can used C, C++ or xC (C with multicore extensions).» [1] p6
xC and Wikipedia
I think it would be «correct» for xC to have a page on Wikipedia. But there are probems. I queried about this at Missing article on the xC programming language. Update Sep23: this url does not point to this concrete discussion anymore. I really don’t know how to find it. Wikipedia Teahouse seems to hide these discussions in some way.
There has been one! It’s at https://en.wikipedia.org/wiki/XC_(programming_language)?oldid=901782500. From the history (here) it is seen that it was «redirected with history» to the XMOS page on 27.Oct.2020 (here) with «multiple issues» like «more citations needed» and «original research» missing from March 2020. Also a comment that the article was too much of a programmer’s manual. There also was a conflict of interest (COI): self promotion issue with it.
I added a query about this at the xCore Exchange, see xC and Wikipedia (31Mar2021).
Background info
(In Nov2020!) I discovered some presentations on David May‘s page. Since you are here I assume you would be first interested in the third presentation.
Communicating Processors Past, Present and Future [24] (2008).
«xC» not mentioned, but language in general (from before the name?)
The Past, CSP, Occam and Concurrency, Processes, Transputer overview, What did we learn? General Purpose Concurrency, Routers, Emerging need for a new platform, The Present, Architecture, Interconnect, Interconnect Protocol, Processes, Processes – use, Process Scheduler, Instruction Execution, Execution pipeline, Concurrency, Fork-join optimisation, Concurrent Software Components, Communication, Ports, Input and Output, Ports, Input and Output example, Timed ports example, Event-based scheduling, Events vs. Interrupts, Summary. XMOS XS1 tile, The Future, Realisation, Concurrent Languages, Architecture, Layering, Optimising distribution, Barrier synchronisation, Load balancing, Concurrent computers and processors, Concurrency
Multicore Architecture [25] (2008)
I think this is a somewhat shorter version of the above. «xC» not mentioned (from before the name?)
XMOS Architecture. xC Language [26] (2010)
Embedded processing, The Present, Architecture, xC, Interconnect, Interconnect Protocol, Threads, Thread Scheduler, Instruction Execution, Execution pipeline, Concurrency – aim, Join-fork optimisation, Communication, Concurrent Software Components, Synchronised communication, Ports, Input and Output, Ports, Input and Output example, Timed ports example, Event-based scheduling, Events vs. Interrupts, Summary, XMOS XS1-G4
XMOS Architecture XS1 Chips [27] (Not dated, 2011?)
«xC» not mentioned, now it’s «C-based language (along with C and C++)«
Introduction, Architecture, Example System, Programming, Scalability, Threads, xCore Architecture, Instruction set, xCore Instruction Encodings, Thread Scheduler, Thread Scheduler, Concurrency and Thread Scheduler, Instruction Execution, xCore pipeline, Communication, Channels and Interconnect, Communication: Addressing, Communication: Messages & Streams, Communication: Sharing, Routing, Routing Example, Link Protocol, Ports, Input and Output, Event-based scheduling, Events, Applications, XMOS XS1-G4, XMOS XS1-L1
David May discusses barrier synchronisation in all four of the above documents. (I have attempted a code for it, see My xC softblinking PWM notes (search for «barrier»)).
Clouds, Things and Robots: … the transputer revisited [28]
Background, CSP, Occam and Concurrency, Transputers and occam, Transputer overview, Transputers, What did we learn? General Purpose Concurrency, Meiko and EPCC 1987, Routers, C104 router, Bristol’s embedded processors (XMOS xCore mentioned), The last 20 years, Clouds, Things and Robots, Why timing matters, Time-determinism, The ‘Random Access’ Memory, Three programs (1-3), Three programs – performance, Sharing memory, Communication, input and output, Software and Algorithms, Architecture revisited, Universality, Universal Parallel Processors, Program Structures, Communication Patterns, Composition, Clos and Benes Networks, Folded Benes Network, Partitions and Synchronisation, Sequential Programs, Programming Languages, Memory management, Processor management, Fault containment, Caches, The need for Innovation, Commodity parallel processing, The open transputing architecture.
David May here really shows multi-processor architecture state of the art as of past, 2017 and future. This is heavy stuff, but if I understood 1/4 then I’m satisfied. (6Dec2020: I could not find this on his home page. I picked it up in a mail group thread).
The XMOS glossary of terms
I recently (Dec2017) discovered this excellent document! 85 definitions over 17 pages. Read this first! See [11].
How does xC compare with C?
Even if I have had it downloaded for years, I rediscovered this lecture from 2014 in 2.2021! This lecture is so good that I took slide 36 and added some text to it. (Download PDF here.)
I didn’t modify to «Use C, xc, C++ or a mixture of all three«. In the figure:> >>
and <: >>
are shown. See this table:
Reserved words
I have not been able to find an updated list in any of the XMOS documentation (I haven’t found any with client
defined in a reserved words list, only by usage, in the References list). Here’s a suggestion. Some may not be a reserved word (like pinsneq
?), some are operators (like :>
) and some are attributes (framed by [[..]]
) – and I may have missed some. I have picked some of these from [2] (but names from the reserved for future list I have not included, since I, in the «future» haven’t come across them in use (like claim
and accept
). Also, I haven’t included standard C-type words like enum
and break
etc. ).
alias |
extends |
pinsneq |
|
[[always_inline]] (*2) |
[[fallthrough]] |
port |
|
buffered port: transfer-width-bits |
[[guarded]] |
… | |
chan |
[[hwtimer]] (*1) |
restrict |
|
… | [[independent_guard]] |
select |
|
chanend |
inline (*3) |
server |
|
[[clears_notification]] |
interface |
service (?) |
|
client |
isnull |
slave |
|
clock |
master |
streaming chan |
|
[[combine]] |
movable |
[[single_issue]] |
@ (counts of clk) |
[[combinable]] |
move |
sync (port) |
:> (read chan or port) |
combine |
[[notification]] |
tile |
<: (write chan or port) |
core |
null |
timer |
=> (after guard) |
[[distribute]] |
on |
timerafter(future) in select case or alone |
? (nullable) |
[[distributable]] |
[[ordered]] |
transaction |
-> interface return params |
[[distributed ...]] |
out |
unsafe |
:> >> read then shift (*) |
[[dual_issue]] |
par |
when |
<: >> write then shift (*) |
(*1) I saw [[hwtimer]]
first described in the video in 221:[1] on 27Jul2021! Sad, I wonder where that documentation is?
(*) Example in AN00192 Application Note [22]
(*2) I discovered [[always_inline]]
in i2c_master.xc
in i2c_lib
on 28Oct2022
(*3) inline
is C99 semantics, allowed by xcc. See i2c_master_ext.xc
in lib_i2c
(29Oct2022)
Single and dual issue
I think [[single_issue]]
and [[dual_issue]]
are desrcibed in a patent by David May [10]. I think the attributes in the language were added for the xCORE-200 family, and I think that they are for the user to be able to have some say on performance(?) Also contextual mention in the 14.3.0 release note here. I have posted a question about this on xCore, here. Still – another reason for XMOS to upgrade the xC docs.
Messages
I show some messages that appear as I code doing all my excellent errors. It’s impossible, I hope (even for me) to end up with exhastive lists this way. Only XMOS could do that. I don’t think the Open Source Licence Agreements code suffices [9]. I have not included obvious messages like lint-like errors.
The alphabetical lists reveal some of the powerful machinery under the hood. I have below added the xTIMEcomposer version at the end of the informal descriptions:
xTIMEcomposer versions since I started this note
- 14.2.4 as of April2017. The lists below has been built based on this version. However, I will not re-check for each update. My note about 14.2.4 is here
- 14.3.0 of May2017. There’s no update caused by it in this blog note. My note about 14.3.0 is here
- 14.3.1 of Oct2017. My note about 14.3.1 is here
- 14.3.2 also of Oct2017. My note about 14.3.2 is here
- 14.3.3 of Apr2018. My note about 14.3.3 is here
- 14.4.0 was not for me, only for XVF3510
- 14.4.1 of 19Dec2020 . My note about 14.4.1 is here
Error messages concerning concurrency etc.
The list only handles messages associated with tasks and concurrency [7]. Believe me, I fell into all of these ditches with no help from my friends. I love this language that does this to me. The next chapter is that for 99% of first time debug trials it just works.
- error: `?’ specified on declarator that is not a reference or a resource
→unsigned ?&led_bits
as a parameter is not legal. This is not how to create a nullable type. The error text must be wrong, though, because?&led_bits
is a reference. Swap the?&
and it compiles fine:unsigned &?led_bits
(14.4.1) - error: a variable declaration prefixed with on must declare an object of type port or clock
→ Placing somethingon
some hw resource can’t just be something like anint
(like I did (14.3.0) - error: alias pointer return type for interface functions are not supported
→ In the XMOS Programming Guide (XM004440A) it says on page 57 in «5.2.4.6 Transferring pointers between parallel tasks» thatalias
is suggested. This error message then seems rather strange. I reported this to XMOS (Ticket 31951). I am struggling with a debug value that I need to pick out of a task after it has died! Withrestrict
this compiled, but it only seemed to work for certain builds. See my xCore Exchange entry Stealing a log value from a dead task. This message also seems to contradict the error: pointer return type must be marked movable, alias or unsafe message (below). But they say in the XMOS Programming Guide that It is undefined behavior for an unsafe pointer to be written from one task and read from another. (13.3.3) - error: all clients to a distributed task must be on the same tile
→ I guess this speaks for itself. Since[[distributable]]
[[distribute]]
sets up a kind of standard inline call on a task’s stack, it cannot be done across tiles. Place it inside a[[combine]] par
instead (14.4.1) - error: both client and server specified
→ Self explaining (14.4.1) - error: buffered specified with port without transfer width for `outP4_leds’
→ I had writtenout buffered port outP4_leds
but it should have beenout buffered port:4 outP4_leds
(14.4.1)
Observe that the below are also meaningful since the transfer width (transfer-width-bits) is the total number of bits that the hardware handles on each xC:>
or<:
operation. On these examples the code must output from a 32 bits value but the hardware then chops it up into four outputs. Observe that this has to go hand in hand with some clock or handshake signal. See [1] and [4]–[5].
on tile[0]: in buffered port:32 p_pdm_mics = XS1_PORT_8B;
out buffered port:32 p_out = XS1_PORT_8A;
- error: call makes alias in function `My_Client’
→ Same channel is used twice in parameter list (14.2.4) - error: call makes alias in function `fade_matchTypeCnt’
→ Two parameters where one of them is in fact contained in the other (14.4.1). Like this, for the first and the last param. Delete the first:fade_matchTypeCnt ( display_ctx.spectrum_analysis_results.ref_sound_src, display_ctx.alarm, display_ctx.spectrum_analysis_results); // Does all MAX_NUM_REF_SOUND_SEQUENCES
- error: call to service prefixed with `on’
→ A service is a task that is actually hardware! Like thestartkit_adc
function. It cannot be placedon
a any resource. Or can it? See error: service called in non multi-tile function («error: service called in non multi-tile function» is also handled below) (14.3.3) - error: can only specify volatile on unsafe pointer target type
→ I guess this has to do with the fact that tasks should be as «thread safe» as possible? (14.3.3) - error: cannot apply [[combine]] to multi-tile par
→ This may be relevant, but see Multi-tile par (14.3.3) (14.4.1) - error: cannot declare reference to interface
→ An interface is a bundle of methods and cannot be of type REFERENCE_TYPE (14.3.0) - error: cannot dereference unsafe pointer outside of unsafe region
→ But this is ok (14.4.1):void testunsafe (int * unsafe p) { printf ("sizeof unsafe ptr = %d\n",sizeof(p)); unsafe { *p++; } }
Observe that sizeof unsafe ptr = 4 and sizeof ptr = 12. See here (Fabriceo 10Apr2020).
- error: cannot output safe pointer
→ I had starred a parameter and tried to output to a port from it. That parameter shold have been&
(14.4.1) - error: case not terminated with a break, return, continue or [[fallthrough]] [-Wswitch-fallthrough]
→ This is the first time that I saw
[[fallthrough]]
forselect case
. It happened when I had terminated the block with two semicolons, like} break;;
(14.4.1)
→ warning: unrecognized attribute `fallthrough’ [-Wunusual-code] for (14.3.3)
Usage (14.3.3) (from warning: case not terminated with a break or return and an info):#pragma fallthrough
Usage with no #pragma (14.4.1):
switch (states_red_LED.state_red_LED) { // #pragma fallthrough // (14.3.3) case state_red_LED_steps_0012: { // The effect of changing intensity_steps is best seen at slow periods: for (unsigned ix = 0; ix < CONFIG_NUM_SOFTBLIKER_LEDS; ix++) { params[ix].period_ms = SOFTBLINK_PERIOD_MAX_MS; } } [[fallthrough]]; case state_red_LED_steps_0100: case state_red_LED_steps_0256: case state_red_LED_steps_1000: { params[IOF_RED_LED].intensity_steps = intensity_steps_list[states_red_LED.iOf_intensity_steps_list_red_LED]; states_red_LED.iOf_intensity_steps_list_red_LED = (states_red_LED.iOf_intensity_steps_list_red_LED + 1) % NUM_INTENSITY_STEPS; write_LEDs_intensity_and_period = true; } break; default : {} break; // Never here, no need to crash } // switch
- error: client specified on chanend type
→ The different ends of achan
do have roles likeclient
orserver
. The concept of achan
is lower level thaninterface
, so we just have to do it right. In the cases I have investigated the number of chanends used by the linker/mapper is the same when the same functionality is changed between use of channels or interfaces. Like a single channel and a in interface that just sends. Still, have a look here. (14.3.3) - error: client interface cannot select on non slave function
→ Usingclient
instead ofserver
in task param field (14.4.1) - error: `c_buttons’ used between two combined tasks ::
→ Observe that in [1] it says that «channels cannot be used between combined tasks.» This cannot be said or learnt too often! However,[[combinable]]
is accepted, but it is[[combine]]
that stops it from being built. This is repeated many times in this document.
When I removed the expliciton tile[x].core[y]
and added a[[combine]
] above thepar
this message appeared. See [[combine]] and [[combinable]] Here’s what’s illegal, withButton_Task
being declared and coded as[[combinable]]
:chan c_analogue; [[combine]] par { // ... Button_Task (IOF_BUTTON_LEFT, inP_button_left, c_buttons[IOF_BUTTON_LEFT]); Button_Task (IOF_BUTTON_CENTER, inP_button_center, c_buttons[IOF_BUTTON_CENTER]); Button_Task (IOF_BUTTON_RIGHT, inP_button_right, c_buttons[IOF_BUTTON_RIGHT]); // ^~~~~~~~~for all~~~~~~~~~~~~~ // error: `c_buttons' used between two combined tasks }
The message is not «between two
[[combinable]]
tasks«, so the below is legal (placement withon
is optional!). I really don’t understand this, but the XMOS engineers must have a solution to run this on the same core and probably not really using the[[combinable]]
attribute. But I thought that one core could only run one low-levelselect
?chan c_analogue; par { // ... on tile[0].core[1]: Button_Task (IOF_BUTTON_LEFT, inP_button_left, c_buttons[IOF_BUTTON_LEFT]); on tile[0].core[1]: Button_Task (IOF_BUTTON_CENTER, inP_button_center, c_buttons[IOF_BUTTON_CENTER]); on tile[0].core[1]: Button_Task (IOF_BUTTON_RIGHT, inP_button_right, c_buttons[IOF_BUTTON_RIGHT]); }
(14.3.3)
- error: client interface cannot select on non slave function
→ In order to use aselect
it has to be in aclient
task, notserver
(14.3.0) - error: combinable function cannot have interface array argument of unknown size
→ Array as[]
is «unkown» (14.2.4) - error: combinable function must end in a `while(1){select{..}}’ or combined `par’ statement
→ Code after break statement of last case ofselect
when[[combinable]]
. Cannot combineselect
s from such tasks. Observe that code is not allowed in any of the starred positions:`while(1)*{select{..}*}*'
(14.2.4) - error: complex select case replicators over interface cases are not supported
→ A case replicator’s interface index contained an expression, likecase (size_t i=0; i < n; i++) i_mine[(i+server_val_c_prev)%n].get_server_val_c (void) -> {unsigned return_value, size_t return_i}
. This also caused «error: interface expression in select case must be a simple variable reference». I think this means that the standard way to code and occamALT
as «fair» (to increase the last used index by 1 modulo div size of array is not possible to code. (The code example here is just a mess in that respect anyhow. But it does generate these two error messages, good for something). Not that I have needed this fair calculation, every time I try a non- [[ordered]] select it magically seems to be «fair» by XMOS design (14.3.3) (See some of this discussed in Nondeterminism) - error: components of multi-tile par must have `on’ specifier or call a service
→ This may be relevant. However, see Multi-tile par (14.2.4) - error: conflicting use of [[combinable]] attribute in declaration of ..
→ Header file and xc file must correspond, of course (14.3.3) - error: core array index must be a constant expression
→ Not valid:par (size_t ix = 0; ix < SOFTBLINKER_SOFTBLINKER_PWM_NUM_CLIENTS; ix++) { on tile[0].core[ix+6]: softblinker_task (if_pwm[ix], if_softblinker[ix]); }
..but for some reason the compiler does not complain if
tile[ix]
(Raised as Ticket to XMOS 30Jun2020) (14.4.1) - error: declaration statement in ‘par’ statement
→ A block starting with apar
must only contain calls to tasks, not declarations (14.2.4) - error: distributed statement must be a call to a distributable function
→ I tried to use[[distribute]]
around apar
of[[combinable]]
functions (14.3.3) - error: distributed task with client and server connection to same logical core is not supported
→ Wording ok as it is. But dwell on it. It’s fantastic! (14.4.1)
→ I have a feeling that it’s a message like this that would constitute is the missing mentioned at Combinable all the way to no no progress (below) - error: function `synchronized’ is not defined in interface `barrier_do_if’
→ Fair enough! (14.4.1) - error: global variable `c_irq_update’ has type chan
→ Achan
cannot be global. It has to be declared inside, likemain
. Same forinterface
(bullet below). However, a placedport
may be declared in global scope. I like it (14.3.3). This message is also used if you type something likevoid ref_1msec_task (chan c_log)
instead ofvoid ref_1msec_task (chanend c_log)
- error: global variable `i_radio’ has type interface
→ Aninterface
cannot be global. It has to be declared inside, likemain
. Same forchan
(bullet above) (14.3.3) - error: incompatible types in output
→ Output to a channel directly from an array. When I packed it in astruct
the compiler was satisfied. I don’t think it checked the other end of the channel. Also seeerror: invalid lvalue as input destination
(below) (14.4.1) (12Feb2022) - error: input from a variable that is neither a channel, port nor timer
→ See xC code examples (No select on state only) (14.3.2, 14.3.3) - error: input from channel with unspecified size
→ I always sentc_irq_rising <: 1;
and tried to receivecase c_irq_rising :> void :
since I was not going to need the value (it is always 1). A solution seems to becase c_irq_rising :> unsigned rising
in which case the compiler does not warn thatrising
is not used later on. So even if channels are «untyped» the compiler checks some. Great! In the XMOS Programming Guide I see channel «input» to void only shown (ie. allowed I assume) on timer timeouts and ports changes, likecase t when timerafter(timeout) :> void:
orp_eth_data when pinseq(0xD) :> void:
orp_in :> void;
(14.3.2) - error: input on an output port
→ A parameter isout port pin
but should have beenin port pin
or the opposite (14.3.2) - error: invalid lvalue as input destination
→ Input from a channel directly to an array is not allowed. The array has to be packed in astruct
. Also seeerror: incompatible types in output
(above) 12Feb2022 (14.4.1) - error: invalid side effect in select
→ Fantastic message! It certainly tells what’s below the hood. I had failed to write aselect case
with the guard=>
but instead written=!
(14.4.1) - error: interface cases that do not select over the entire array are not supported [[combinable]] functions
→ (14.3.3). Code example that causes several error messages:select { case (size_t index=iof_next_first_2; index < (n+iof_next_first_2); index=index+1) i_mine[index%n].do_server_2 (void) : { iof_next_first_2 = index + 1; // error: complex select case replicators over interface cases are not supported // error: interface expression in select case must be a simple variable reference // error: interface cases that do not select over the entire array are not supported [[combinable]] functions } break; }
- error: interface declaration initialized
→ It’s not legal to make a local array of interface that would go like this:
conn_prot_if_t conn_3 [3] = {conn_lower[0], conn_lower[1], conn_horisontal[0]};
If I instead try to initialize one of the elements of the array
like conn_3 [0] = conn_lower[0];
then I instead get
error: invalid lvalue in assignment.
So I have no idea how to really make a localinterface
array consisting of knowninterface
s. However I also, in this case get
error: assignment to resource that is not qualified unsafe
so this at least explains why. They want to take the whole interface array through from the top level, not just build one. I tried to make anunsafe
region, but that only takes me back to square one - error: interface expression in select case must be a simple variable reference
→ See the code case shown with «error: complex select case replicators over interface cases are not supported» (14.3.3)
Another example:select case i_conn_session[iof_0].start_work (const work_param_t work_param_par) -> work_result_t notified_return :
whereiof_0
is anunsigned
variable in scope. It would also not accept parameters coming in asconst
(or not) valued parameters. I assume this is to keep the interface usage analysis simple or even in some cases, possible (14.4.1) - error: interface function parameter cannot contain interface type
→ Any interface parameter must be given as an entry in the task’s parameter list. If you have some interface functions that use an interface, and some that don’t, then the only way to distinguish this is by some name convention (14.3.3) - error: interface function `do_io_server’ used in more than one case
→ I had g_conn_if_t i_conns[NUM_CONNS_PER_NODE] declared as an array. Arrays cannot be picked out component for component in, like three cases. I guess there is a readon for it! (14.4.1) - error: interface parameter must have `server’ or `client’ modifier
→ xC concerns about the roles, to get multi-task communciatiion patterns right (14.3.0) - error: interface used as both client and server in one task
→ Message should be ok, but I got this together with a «xcc1: problem recovering from program error, further error messages may be suppressed». I posted this as a ticket to XMOS (it became Issue 31339) (14.3.3) - error: interface used in two tasks as client
→ I had 2 clients to a server, and parameterised both as my_interface[0] instead of one my_interface[0] and one my_interface[1] (14.3.0) - error: interface used in two tasks as server
→ I made this code just to test something (14.4.1)void worker_task_scheduler (server worker_if_t i_worker[NUM_WORKERS]) { // cores,timers,chanends 5,6,12 par { while (true) { worker_task (i_worker[0], 0); } while (true) { worker_task (i_worker[1], 1); // note: other use is here } while (true) { worker_task (i_worker[1], 2); // ../src/main.xc:935:32: error: interface used in two tasks as server } } }
- error: invalid lvalue as input destination
→ I was trying to fill data from a channel into an array, in order to try to implement an occam type counted array protocol (search for that term in My Beep-BRRR notes, there might be a solution in there). The below does not work. Since a channel is not typed, this must be something that xC has added as a «quasi type» type check. It’s a rather good idea, since C does not allow an n-dim array where n is not a constant (here) (14.4.1 19Dec2021)mic_sample_t arr[10]; arr[0] = 1; // just to test, compiles ch_ab_bidir :> arr; // error: invalid lvalue as input destination
However, this seems to work. I have to relate to chuncks of hard compiled dimensions;
typedef struct { mic_sample_t arr[10]; } array_10_t; array_10_t array_10; array_10.arr[0] = 1; // just to test, allowed ch_ab_bidir :> array_10; // compiles
- error: local variable `ch_some_name’ has type chanend
→chan
is used when thechannel
, containing twochanend
s is declared and the task is described and started. However, a task knows them aschanend
s only (14.4.1) 18Dec2023 - error: missing case for interface function
→ But I haven’t finished the writing !-) (14.4.1) - error: multi-tile main must consist of single non-empty par statement
→ (24Jun2023) A hw function likeconfigure_clock_src
must be run before thepar
task that uses it is set up in apar
. So the example in document I2S/TDL Library (XM007055) forlib_i2s
, page 11 top does not work in this case: When someon tile[n]
is used then theconfigure_clock_src
cannot be run before the firstpar
, but inside some lateron tile[n]
block (14.4.1) - error: output to an input-designated port
→ A parameter isin port pin
but should have beenout port pin
or the opposite (14.3.2) - error: parameter `c_barrier’ has type containing chan
→ Myc_barrier
should of course have been of typechanend
(14.4.1) - error: parameter cannot be reference to resource
→ The parameter in this case was in interface which basically is a bundle of methods (14.3.0) - error: parameter is not a reference type and contains a resource
→ A resource is something that is placedon
something. This must done inmain
or at the top level. Also, if a struct contans a resource, like a timer, then in the function it it must be by reference (with an ‘&’, even if it’s not modified.
Example: (14.4.1),typedef struct { unsigned some_cnt; timer buzzer_tmr; time32_t buzzer_time_ticks; unsigned buzzer_cnt; unsigned buzzer_freq; } buttons_ctx_t; void now_button_beep (buttons_ctx_t &buttons_ctx) { buttons_ctx.buzzer_cnt = buttons_ctx.buzzer_freq / 50; buttons_ctx.buzzer_tmr :> buttons_ctx.buzzer_time_ticks; // immediately } bool every_50 (buttons_ctx_t buttons_ctx) { // '&' also needed here! return ((buttons_ctx.some_cnt % 50) == 0); }
Example: (14.3.0)
// Instanciating in user top level file: out port p_probe = on tile[0]: XS1_PORT_1L; // and params like this in callee: out port p_probe // This is "the port" // and usage: p_probe <: 1; // // Or alternatively with type defined in a header file: typedef struct probe_pins_t { out port probe_when_irq; } probe_pins_t; // and instanciating in user top level file: probe_pins_t probe_config = { on tile[0]: XS1_PORT_1L }; // and params like this in callee: probe_pins_t &p_probe // Observe reference, this error message if not // and usage: p_probe.probe_when_irq <: 1;
- error: passing arg 1 of `digitalWrite’ changes direction of type
→ A parameter was «in» or «out» but the opposite type of port was sent into the function (14.3.2) - error: passing arg 2 of `Button_Task’ changes port from buffered to unbuffered
error: passing arg 1 of `client_task_buttons’ changes port from unbuffered to buffered
→ Have to do within buffered port:1 p_button
versusin port p_button
(14.4.1) - error: passing arg 2 of `client_task’ discards direction qualifier from type
→ I had writtenport inP1_button
as a parameter whereout port inP1_button
would have been correct (14.4.1) - error: pattern variable cannot be used with array of unknown size
→ Standard replicated select case works only on an interface array of known size. Unkown size array[]
is allowed as param if it’s not[[combinable]]
(14.2.4) - error: pattern variable declaration only allowed in select case
→ Not allowed to index a guard array plus index to interface array (14.4.1)case guard[unsigned ix]=>iface[ix].is_data(const unsigned temp)->unsigned ret: { // ... } break;
- error: pointer return type must be marked movable, alias or unsafe
→ This is from an interface function. However, this seems to contradict the error: alias pointer return type for interface functions are not supported (above) (14.3.3) - error: port used in more than one case
→ Example: you cannot let button press and a button release have one select case each. There is a mechanism to let them both be handled in oneselect case
, like
case is_stable => p_button when pinsneq(current_val) :> current_val: {..} break;
(14.4.1) - error: Program on «tile[1]» contains a reference to resource «p_sda» not resident on that tile
→ That port was placed ontile[0]
(14.3.3) («Error» with capital ‘E’:-) - error: `return’ within a par
→ A block starting with apar
must be a block, enclosed by curly brackets (14.3.0) - error: port:4 specified without buffered for `outP4_leds’
→ I had writtenout port:4 outP4_leds = on tile[0]: XS1_PORT_4F;
(14.4.1) - error: select case has guard without [[independent_guard]] case attribute or [[guarded]] interface function attribute
→ Interface method not tagged as[[guarded]]
while there is a boolean guard in the code (14.2.4) - error: select case in a [[distributable]] function which is not on an interface
→ A[[distributable]]
function is neither allowed to have aselect case timerafter
orpinsseq
or the likes, since they are not defined in aninterface
(14.3.3) - error: select on notification within combinable function select case
→ Nested select in a task that is tagged as[[combinable]]
(14.3.3)
→ The error message used as such is misleading since this also goes for a[[distributable]]
task. So, the message should have been (I’ve reported this to XMOS (issue 31441)):
error: select on notification within combinable or distributable function select case
→ However, it does make sense to avoid a deadlock from usingselect
,client
,server
andslave
incorrectly. I will dicuss this in My xC softblinking PWM notes some time after late Aug2020 - error: server specified on chanend type
→ The different ends of achan
do have roles likeclient
orserver
. See same message forclient
(14.4.1) - error: service called in non multi-tile function
→ I got this when the «service» (not a task in itself, this is a somewhat vague concept to me, but I think it’s closer to HW than other xC code) calledstartkit_adc
was placed wrongly in apar
. Also see error: service called in non multi-tile function on xCore Exchange and «error: call to service prefixed with `on’ » (above). (14.3.3) - error: slave modifier without [[notification]] attribute is not supported
→ Interface method not tagged as[[notification]]
while the slave (server) code does contain a notfication. Needed to ensure atomicity of session command→notification→read (14.2.4) - error: statement cannot be `on’ a tile unless part of top-level par
→ I already had anon tile[0]:
outside the par, so I was not allowed to do anon tile[0].core[4]:
below it (14.4.1) - error: statement in combinable par must be a call to a combinable function
→ As the text says. However, the[[combine]]
needs not be in the enclosingpar
, it’s enough that the enclosingpar
is in pair with another[[combine]] par
. I don’t really understand this, but it’s probably because any[[combinable]]
either must be placed withon
or in a[[combine]] par
. (14.3.3)
The fix: try to move[[combine]]
to just above the innerpar
of a replicatedpar
, like this:par { #define IOF_ROW_VER ((iof_row + (NUM_ROWS-1)) % NUM_ROWS) #define IOF_PAIR ( iof_col / NUM_NODES_PER_PAIR) #define IOF_PAIR_HOR ((IOF_PAIR + (NUM_PAIRS_PER_ROW-1)) % NUM_PAIRS_PER_ROW) #define IOF_ROW_START 0 on tile[0]: par (unsigned iof_row=IOF_ROW_START; iof_row<(IOF_ROW_START+NUM_ROWS_PER_TILE); iof_row++) { par (unsigned iof_col=0; iof_col<NUM_COLS; iof_col=iof_col+NUM_NODES_PER_PAIR) { [combine] par { // Some task or tasks in here. Use: // iof_row, iof_col, // IOF_ROW_VER, IOF_PAIR, IOF_PAIR_HOR since, // not even local const may be declared inside a par } } } }
- error: source array smaller than destination for argument 1 of `some_Server’
→ A server hasn’t got the necessary clients, as declared in the server declaration. I think they must be declared at compile time (which I like) (14.2.4)
→ Also for this construct in the parameter field of a function:
out port p_ss[num_slaves], static const size_t num_slaves
where the dimensionnum_slaves
of the array must correspond to the valuenum_slaves
. Observe thatnum_slaves
may be used by the caller to set a static size of an array. Nice for libraries, nice to avoid cross-function defines like#define NUM_SLAVES 1
- error: statement placed on a core must be call to combinable function
→ There is one hardware thread per logical core. If (more than) one task is placed on a core the select loops will be merged (so must be[[combinable]]
) into one select loop that’s owned by that HW thread. Remove thered
(example):on tile[0].core[4]:
(14.2.4)
→ The error message is misleading since a[[distributable]]
task may also be placed on a core. So, the message should have been (I’ve reported this to XMOS (issue 10842)):
error: statement placed on a core must be call to combinable or distributable function - error: STRUCT field `i_spi’ may not be of type `interface’
→ A struct may not contain an interface element. An interface parameter must be a unique parameter (14.3.2) - error: `some_channel’ used in more than two parallel statements (byte range 4..8)
→ Message is fine. The opposite («not used in two») is a warning (see below). Last part is explained here. (14.2.4) - error: Symbol p_ss[0] is a duplicate port declaration.
→ I got this when I initialised aport
array of two bit-wide ports with the same port values, likeout port p_ss[NUM_SPI_SLAVES] = {XS1_PORT_1A, XS1_PORT_1A};
(14.3.2) - error: timer used in more than one case
→ The sametimer
is used in more than oneselect case
. Two differenttimer
s is of course ok. Even if a guard only would allow selecting any one of them at a time, like(value==1)=>
and(value==2)=>
. Observe that even if you then would «use one more timer» in the xC code then it’s still a single «physical» timer that’s used, since it’s basically the same «physical» timer with two different timeouts. Question: why do they then disallow «using the same timer» in two select cases in the xC code? (14.3.2)
→ I queried about this in the xCore Exchange forum: Why «error: timer used in more than one case» (9May2020) - error: trying to call interface function from a server interface
→ Myinterface
was declared used atserver
side, but it wasclient
side. Since the usage in code also wasclient
side, my declaration was wrong. It should have beenclient
.. in param list, notserver
(14.3.0) - error: trying to call interface function from something that is not an interface
→ Calling from server side an interface function that should have been listened to (14.3.3) - error: trying to call slave function from a client interface
→ This message is as simple as the text goes. A slave function should be called by the server, not the client! Also, you cannot pick up a single[[notification]] slave ..
without wrapping it in aselect case notify
type of code, because this would tell the compiler that you are aware of your role as aclient
. Aside: However, I got this error message once, I think, as an erronoeus error message! See it discussed on xCore Exchange at Error reporting on erroneous «:> void» in select component (14.3.3) - error: use of `millis_state’ violates parallel usage rules
→ Faulty access of a global variable in two parallel tasks.
millis_state
isextern
in a header file, declared in one xc file and used in it and in another xc file. This is the non-global suggestion discussed in xCore Exchange Porting the Arduino millis(), possible? Search for «parallel usage» here for more (14.3.2) - error: use of `outP4_leds’ violates parallel usage rules
→ Faulty access of a globalport
(it’s always global) in two parallel tasks.
This may happen in a sequentialpar
even if you have qualified thatport
parameter to benull
in all other cases than one. You can do a test likeif ((i_row==0) and (i_col == 0))
inside apar
, but it doesn’t help (14.4.1) - error: void value not ignored as it ought to be
→ Came together with, and disappeared with «error: trying to call slave function from a client interface» (above). It was this error message that set me on tract to remove that:> void
(14.3.3) - error: `?’ specified on declarator that is not a reference-. or a resource
→ It is not allowed to use an optional parameter in aninterface
call.
Not:typedef interface my_i {void command (const unsigned command, const unsigned ?value);} my_i;
(14.3.2) - error: [[indepedent_guard]] attribute in combinable function
→ A[[combinable]]
task must have[[guarded]]
in theinterface
definition, not the odd[[independent_guard]]
for eachselect case
. Open all folds and search for an[[independent_guard]]
example (14.3.3) - xmap: Error: Symbol inP_button_center is a duplicate port declaration.
xmap: Error: previously declared as p_ss[0].
→ Aliasing of two names on the same port is not allowed (14.2.4) - xflash: Error: F03136 No devices attached – Cannot run the enquirer until a device is found
→ No board connected
Warning messages concerning concurrency
- warning: argument 1 of `spi_master_2′ slices interface preventing analysis of its parallel usage (for other cases it could add info like the bound parameter is not `const’ (14.4.1))→ This warning excels in telling about what’s under the hood. Here’s the offending code:
on tile[0].core[6]: spi_master_2 (i_spi, NUM_SPI_CLIENTS, p_sclk, p_mosi, p_miso, SPI_CLOCK, p_spi_cs_en, maskof_spi_and_probe_pins, NUM_SPI_BOARDS);
I had messed up the figures here; it’s not possible to do parallel usage check when the interface dimension (it was an array) and the number of clients were not the same. This warning impresses me, parallel usage checking I certainly had with occam in the early nineties. But it was in many ways a simpler language. XMOS has certainly done a good job here. And I am lucky enough to work with this xC tool! Search for «parallel usage» here for more. (14.3.2) - warning: cannot have ordered main select in combinable function (directive will be ignored)
→[[ordered]]
aboveselect
as described in the text not legal (14.3.2) - warning: using `with’ as an identifier is deprecated and will not be supported in the future
→ Fair enough. But I wonder, what was it sued for? See note: Did you misplace the keyword `with’? (14.4.1) - warning: ignoring attribute `combine’ (cannot apply to function)
→ I should have written[[combinable]]
above my task.[[combine]] par
is the proper pair. (14.3.3) - warning: ignoring attribute `combinable’ (cannot apply to statement)
→ I tried to do a replicated[[combine]] par
to just an interface output statement. I must in case wrap it into a combined task proper (14.4.1( - warning: ignoring combine attribute on statement which is not a par
→ Attributes like[[combine]]
should be placed before apar
, not on the call line (14.3.3) - warning: parameter `len’ used as array bound should be unsigned for maximum performance
→ (Not really about concurrency..) An array parameter may have its len (size) defined by a parameter following thearray[len]
param, like this:static const unsigned len
. I had usedint
instead ofunsigned
. However, chapter 5.1.3 Variable length arrays in [1] showsint
usage here. (14.3.3) - warning: ignoring ordered attribute not on select or par [-Wunusual-code]
→ Message is fine.[[ordered]] select
does not make sense because I had flawed theif then else
levels - warning: `some_interface’ not used in two parallel statements (byte range 4..8)
→ Message is fine. The opposite («used in more than two») is an error (see above). Last part is explained here. (14.2.4) - warning: within select statement containing `default’, a case that inputs on an unbuffered port may never be taken
→ Message is fine. I just changed fromin buffered port:1 inP1_button
toin buffered port:1 inP1_button
I was just testing a default in a select, something I never do, because I have not needed busy polling. Also see main select in combinable function cannot have default case
Info messages concering concurrency
- note: Did you misplace the keyword `with’?
→ See warning: using `with’ as an identifier is deprecated and will not be supported in the future (14.4.1) - note: interface function `receiveDone’ can only be omitted from select if marked as [[guarded]]
→ Message is fine! Very interesting! I mistakenly had an interface function declared in the interface descriptiion, but had forgotton to put the code for it in theselect
. I don’t know why I would want to omit the code there, but I guess it must be in some situation where it’s logical to also mark it as[[guarded]]
since the guard perfectly well may be a constantfalse
?
Other error messages
- Binary (xs1_revb) does not match architecture (xs2_reva)
→ Trying to load startKIT code om xCORE-200 eXplorerKIT
Simply change in the makefile fromTARGET = STARTKIT
toTARGET = xCORE-200-EXPLORER
and rebuild! It least it works if you don’t have any other HW to relate to - xrun: Problem in connection to device
Cannot connect, device is in use by another process
→ I think this is often seen with ERROR: socket bind.(0)
→ Kill one or both of xrun and xgdb in Activity Monitor (Mac OS X / macOS). For some reason they haven’t been terminated when they should. For me I get xTIMEcomposer to hang with spinning wheel quite often during download (or other) and I have to force terminate it. That’s when it happens. Even with 14.3.0 in August2017. Solution by mr. infiniteimprobability on xCore Exchange: ERROR: socket bind.(0). I first noticed it here - ../src/main.xc:(.dp.data+0x1c): Error: Meta information («_i.button_if.button.max.nstackwords») for function «_i.button_if.button.max» cannot be determined.
../src/main.xc:(.dp.data+0x1c): Error: lower bound could not be calculated (function is recursive?).
→ I had used achan
to send from button tasks. Theselect case
where thischan
was picked up was guarded by an expression. When I replaced the chan with aninterface
that sent from the buttonclient
task then the compiler had me add[[guarded]]
in theselect case
. This guard later was the cause of this error message because the send was based on a timerafter with no acknowledge back. In theory the button task could send more than once and then this might actually be recursive. With an interface this is not acceptable, but for achan
it was. The underlying semantics is different, I guess. It is impressive that XMOS pick up this. Update: I am not certain if this reasoning is correct, since this example compiles ( folds to go there). (14.3.3)
Other messages
- Warning: Support for xCORE-XA is to be removed from the next major version of xTIMEcomposer. Issued by (14.4.1)
→ This comes when compiling for the TARGET = xCORE-XA-MODULE, which contains the xCORE-XA device that has 8 logical cores and one ARM Cortex-M3 processor. Example code from XMOS Application note AN00141 («xCORE-XA – Application Development») and AN00142 («xCORE-XA – xCORE ARM Bridge Library»), AN00143, AN00144, AN00145 and AN00146 (plus, perhaps more) - Error: F03122 No Arm Binary supplied, please use option –arm-binary
→ This comes from the xflash tool when I just recompiled with TARGET = xCORE-XA-MODULE and had not yet done as AN00141 told (14.4.1) - Invalid build configuration: Default
Valid build configurations are:
/Applications/XMOS_xTIMEcomposer_Community_14.4.1/build/xcommon/module_xcommon/build/Makefile.common:183: *** Invalid configuration. Stop.
→ I had by coincidence added a comment in Makefile, like this:
TARGET = MIC-ARRAY-1V3 # Use MYTARGET=MIC_ARRAY below
I guess this is a criminal offence against
make
. But it took me hours to find out!
# Comments are on separate lines in make
(14.4.1)
Missing error messages
Combinable all the way to no no progress
See My xC combined combinable notes. Also search for «error: distributed task with client and server connection to same logical core is not supported» here.
«It’s not you» errors
Multi-tile par
Any multi-tile par message may be ok. However, this shows an example when it’s issued once (or twice) too much. I tried digging into this in July2018 with xTIMEcomposer 14.3.3. Now I understand that the compiler reports this erroneously:
// error: cannot apply [[combine]] to multi-tile par WORKS // ============================================= ============================== 01 interface button_if_t i_but[3]; 02 par { 03 interface button_if_t i_but[3]; on tile[0]: { 04 [[combine]] [[combine]] 05 par { par { 06 on tile[0].core[0]: handle (i_but); handle (i_but); 07 on tile[0].core[0]: button (i_but[0]); button (i_but[0]); 08 on tile[0].core[0]: button (i_but[1]); button (i_but[1]); 09 on tile[0].core[0]: button (i_but[2]); button (i_but[2]); 10 } } 11 } 12 }
The compiler thinks that the left is multi-tile par while it is not. However, the right part works . The compiler (linker/mapper?) should have seen that the leftmost are all tile[0]
and not different tiles. The rightmost code came out of the post that I started on xCore Exchange: error: cannot apply [[combine]] to multi-tile par (thanks to mr. infiniteimprobablity with the cat for the cue).
Don’t think that you can add .core[0]
to rightmost line 03 or do on core[0]:
in rightmost lines 06-09. I think it’s either on tile[n]:
alone or the full on tile[x].core[y]:
.
The code does not work with a chan
version(!?) Here is the complete code:
The code: Multi-tile par
#if defined TEST_CANNOT_APPLY_COMBINE_TO_MULTITILE_PAR #include <platform.h> #include <timer.h> // XS1_TIMER_HZ etc typedef signed int time32_t; typedef interface button_if_t { void but (int x); } button_if_t; [[combinable]] void button (client interface button_if_t i_but) { timer tmr; time32_t time; tmr :> time; while (1) { select { case tmr when timerafter(time) :> void: { i_but.but(time/XS1_TIMER_KHZ); // ms time += XS1_TIMER_HZ; break; } } } } [[combinable]] void handle (server interface button_if_t i_but[3]) { while (1) { select { case i_but[int i].but (int val) : { break; } } } } #define DO_PLACED 3 int main (void) { interface button_if_t i_but[3]; #if (DO_PLACED == 1) [[combine]] par { // ^~~~~ error: cannot apply [[combine]] to multi-tile par on tile[0].core[0]: handle (i_but); on tile[0].core[0]: button (i_but[0]); on tile[0].core[0]: button (i_but[1]); on tile[0].core[0]: button (i_but[2]); } #elif (DO_PLACED == 2) par { on tile[0]: { [[combine]] par { handle (i_but); button (i_but[0]); button (i_but[1]); button (i_but[2]); } } } /* Constraint check for tile[0]: Cores available: 8, used: 1 . OKAY Timers available: 10, used: 1 . OKAY Chanends available: 32, used: 0 . OKAY Memory available: 65536, used: 1816 . OKAY (Stack: 384, Code: 1204, Data: 228) Constraints checks PASSED. Build Complete */ #elif (DO_PLACED == 3) par { on tile[0]: { [[combine]] par { handle (i_but); button (i_but[0]); button (i_but[1]); button (i_but[2]); } } } #endif return 0; } #elif defined TEST_CANNOT_APPLY_COMBINE_TO_MULTITILE_PAR_CHAN #include <platform.h> #include <timer.h> // XS1_TIMER_HZ etc [[combinable]] void button (chanend c_out) { timer tmr; int time; tmr :> time; while (1) { select { case tmr when timerafter(time) :> void: { c_out <: (time/XS1_TIMER_KHZ); // ms time += XS1_TIMER_HZ; break; } } } } [[combinable]] void handle (chanend c_but[3]) { int val; while (1) { select { case c_but[int i] :> val: { break; } } } } #define DO_PLACED 2 int main (void) { chan c_but[3]; #if (DO_PLACED == 1) [[combine]] par { // ^~~~~ error: cannot apply [[combine]] to multi-tile par on tile[0].core[0]: handle (c_but); on tile[0].core[0]: button (c_but[0]); on tile[0].core[0]: button (c_but[1]); on tile[0].core[0]: button (c_but[2]); } #elif (DO_PLACED == 2) par { on tile[0]: { [[combine]] par { handle (c_but); // ^~~~ note: other end is used here button (c_but[0]); // ^~~~ error: `c_but' used between two combined tasks button (c_but[1]); // ^~~~ error: `c_but' used between two combined tasks button (c_but[2]); // ^~~~ error: `c_but' used between two combined tasks } } } #endif return 0; } #endif
No liveness analysis
The compiler will help you avoid deadlocks by trying to enforce correct patterns, seen by the error messages above.
However, it will not do liveness analysis and detect deadlock (or livelock). I have seen it allow me to input and output on the same chanend
in the same task without any complaints. I will investigate more. Theoretically the channel could have changed direction, but I don’t know if that’s legal xC. Later: It is. See first code example below.
XMOS series of processors
See XMOS series of processors in My XMOS notes.
timerafter (future)
Observe that timerafter(time)
must be after some time in the future at the time of the timerafter
.
Even if the below code makes sense, if you want to trigger immediately. But timerafter("yesterday")
does not imply any waiting, which the after implies – and in effect timeafter("a cycle or two ago")
does nothing more that trigger the select case (like ALT TRUE & SKIP
in occam). xC doesn’t have that construct, but this is probably as «cheap» (costs little):
tmr :> time_ticks; // IMMEDIATELY while (1) { select { case tmr when timerafter (time_ticks) :> void : { // NOW } break; } }
This is discussed in 165:[Timerafter to now and skew].
Reinterpretation
It was only after discovering Douglas Watt’s excellent book on xC [14] that my eyes were opened for reinterpretation. Here is some code inspired from [14]:
void printitinstead (const int value) { printf ("Value 0x%08X\n", value); // Prints: Value 0x47494554 = "GIET" ("TEIG" backwards) } void transmitMsg ( const char msg[], // sizeof(msg) * 4 must be nwords! static const int nwords) // So 14 chars and 3 words will lose 2 chars! { for (int i=0; i<nwords; i++) { printitinstead ((msg, int[])[i]); } } transmitMsg ("TEIG", 1);
Read about it in [14] at page 12, or without code examples in [3]. I’ve copied these sentences from [14]:
A reinterpretation causes a variable to be treated as having a different type, but it undergoes no conversion. .. In this example, the size of the integer array is determined at run-time.
Observe that if 10 bytes are sent over to be reinterpreted the last two will not be handled. To me the compiler would establish the array size at compile-time!? This also goes for this:
Aside: Observe that the second parameter into transmitMsg
I have changed in the example into static const
. This may be used in the code to declare an array like int my_int_array[nwords]
. Read about it in [1] in chapter 5.1.3 Variable length arrays. It is very nice and useful!
Replicated par and placements
Problem 31198
This chapter is a result of the reported XMOS Ticket 31198, based on code in xC code examples [N clients client-server interface code] (called Ticket31198Code below). XMOS has responded and allowed me to publish the findings here. xTIMEcomposer version at the moment is 14.3.3.
- In Ticket31198Code there is a situation
(DO_PLACED == 1)
with replicatedpar
that does not work. The code in the fold (below) by XMOS shows how[[combine]]
may be used instead, «under the assumption that there isn’t an explicit initial handshake, and guarded functions aren’t used». See the chapter [[combine]] and [[combinable]] that I wrote after this response from XMOS - In Ticket31198Code I have added this new XMOS code with
(DO_PLACED == 2)
to also avoid replicatedpar
The code: Using [[combine]] instead of core placement
// XMOS code during Ticket 31198 with some comments added #include <platform.h> #include <stdio.h> interface i { void send(int x); }; [[combinable]] // [[distributable]] seems to work also with [[combine]] par of them! void s(server interface i i) { while (1) { select { case i.send(int x): printf("%d\n", x); break; } } } [[combinable]] // Try [[distributable]] here and see the error message! void c(client interface i i) { timer tmr; int time; int first = 1; tmr :> time; while (1) { select { case first => tmr when timerafter(time) :> void: i.send(123); first = 0; break; } } } int main (void) { interface i i[2]; // Using the combine attribute instead of core placement // Under the assumption that there isn't an explicit initial handshake, and guarded functions aren't used par { [[combine]] // With this: one core, one timer and one chanend less par { c(i[0]); c(i[1]); } [[combine]] // With this: one core and one timer less par (int j = 0; j < 2; j++) { s(i[j]); } } return 0; }
(DO_PLACED == 1)
, so it’s about problems around the replicated par
construct:
- Interface index is wrong on a
guarded
interface function.
In your codeindex_of_client
will be wrong when it comes out of thei_notify.start_collect_data case
statement. This only happens when theguarded
attributed is used - Core placement on the same core breaks interface function calls when done across separate
par
statements.
What works as expected is
par {on tile.core; on tile.core}
and
par {on tile.core par {on tile.core}}
. What doesn’t work is
par {on tile.core} par {on tile.core}
. This doesn’t affect your example program - Using the
par
replicator index for core placement is unimplemented.
So you can’t do
on tile[0].core[i]
inside apar (int i
style replicator. This is a known issue and doesn’t affect your example program
The above are currently internal engineering bug reports, but you are welcome to share the details with the community on your blog.
[[combine]] and [[combinable]]
Observe that these are synonymous terms:
channels cannot be used between combined tasks ==
channels cannot be used between tasks on the same core, since combined tasks indeed run on the same core. Just like for distributed tasks.
More on [[combine]] and [[combinable]]
[[combinable]]
perhaps needs a [[combine]] par
to do anything for my code. I didn’t even know about [[combine]]
and at the same time also discovered [[distribute]]
with this example. I had read about this in [1] but it obviously hadn’t fastened in my grey cells. When I look it up there it also says that «channels cannot be used between combined tasks.» I have updated in the Reserved words list above. (However, [[combinable]]
is accepted, but it is [[combine]]
that stops it from being built.
I posted [[combinable]] without [[combine]] on the xCore Exchange (June2018). There may be something to pick up there!
Here’s a summary so far (17Jun2018):
- In the code (fold, below) you will see how, when a chan is used one
chanend
must reside on a different core than where the combined select runs. (Observe that this is a transformation that the compiler does, it takes your code’s selects and comibines them into one machine code select). I think not allowing bothchanends
in there is because when severalselect
are[[combined]]
into one low levelselect
then that sameselect
can not send on achan
to itself. It would deadlock. No reason for an advanced modeling tool to catch this one. It’s avoided by the code pattern used. It’s throughly described in [1] where is says that «channels cannot be used between combined tasks.» However,[[combinable]]
is accepted, but it is[[combine]]
that stops it from being built. - A
[[combinable]]
decoration on a task is «used» in one of two ways, as shown in the code both above and below- By a
[[combine]] par
and no explicit placement likeon tile[0].core[0]: ...
, - By explicit placements like
on tile[0].core[0]: ...
- Again, it is accepted for tasks that use channels, but using it as such, with
[[combine]]
is not allowed
- By a
- Since
chanend
is a quite limited resource (each tile has 32. Period) then usinginterface
instead of achan
would make sense. When I changed from 1.) usage of achan
with a[[combinable]]
task that was placed on a separate core to 2.) aninterface
of the sender/client
to the receiver/server
then I saved 1 core and 2chanend
! I have shown these examples in the xCore Exchange entry I mentioned above. And here’s the newinterface
definition for that untypedchan
; :typedef interface irq_if_t { // interrupt inut port from RFM69 radio void pin_rising (const int16_t value); // value is signal strength RSSI if measured (dB) } irq_if_t;
The code: [[combine]] and [[combinable]]
eat
task;
#include <platform.h> #include <stdio.h> typedef interface button_if_t { void send (int x); } button_if_t; [[combinable]] void ser (server interface button_if_t i) { while (1) { select { case i.send(int x): { printf("%d\n", x); break; } } } } [[combinable]] void cli (client interface button_if_t i, chanend x) { timer tmr; int time; int first = 1; tmr :> time; while (1) { select { case first => tmr when timerafter(time) :> void: { i.send(123); x <: 123; first = 0; break; } } } } // From [1] // A combinable function must obey the following restrictions: // * The function must have void return type. // * The last statement of the function must be a while(1) statement containing // a single select statement. // If a function complies to this format then it can be marked as combinable by adding // the combinable attribute: [[combinable]] // Not needed, makes no difference void eat (chanend x[2]) { int val; while (1) { select { case x[int i] :> val: { break; } } // val++; // Not legal in [[combinable]] } } int main (void) { interface button_if_t i[2]; chan x[2]; par { eat(x); // Cannot be used within the [[combine]] below. Both tasks cannot be combined // Using the combine attribute instead of core placement // Under the assumption that there isn't an explicit initial handshake, and guarded functions aren't used [[combine]] // With this: one core, one timer and one chanend less par { cli (i[0],x[0]); cli (i[1],x[1]); } [[combine]] // With this: one core and one timer less par (int j = 0; j < 2; j++) { ser (i[j]); } } return 0; }
The combined code: 6 to zero chanends
I guess that in order to try to understand [[combinable]]
, [[combine]]
and how chan
and interface
influence, I guess, both the code that’s generated and the chanend
s usage – then this code is rather valuable. There are two cases (with 8 different placements each), one with chan
and one with interface
.
The same functionality uses from 6 to zero chanend
s! Since chanend
s are a limited resource (32 per tile
) then this is interesting.
Another matter is that two of the placements I have done is not allowed when used with chan
, but works with interface
usage. Or is this two error cases too much? (xTIMEcomposer 14.3.3)
I have given up on seeing the whole pattern here. (But have a look at the xCore Exchange post I started, there may be something to pick up there, see The combined code: 6 to zero channels!)
However when I look at the code I think it looks rather reasonable! I have testet and run all the OK code on a startKIT.
There is a side-by-side comparison in this document: 41_fig1_the_combined_code__6_to_zero_channel_oyvind_teig.pdf
- I also have an earlier attempt at xCore: Calculating number of chanends
- Plus, it’s possible to save chanends by switching a channel at run-time, by using the same chanend in both directions. This is discussed below, here
#if defined TEST_CHAN_AND_COMBINE_TEST #include <platform.h> #include <stdio.h> #include <timer.h> // XS1_TIMER_HZ etc #define DEBUG_PRINT_TEST 0 #if (DEBUG_PRINT_TEST == 1) // Uses 1 timer and one chanend (not counted below) #define debug_print(fmt, ...) do \ { if(DEBUG_PRINT_TEST) printf(fmt, __VA_ARGS__); } while (0) #else #define debug_print(fmt, ...) #endif [[combinable]] void button (chanend c_out) { timer tmr; int time; tmr :> time; while (1) { select { case tmr when timerafter(time) :> void: { c_out <: (time/XS1_TIMER_KHZ); // ms time += XS1_TIMER_HZ; break; } } } } [[combinable]] void handle (chanend c_but[3]) { int val; while (1) { select { case c_but[int i] :> val: { debug_print ("handle: from %d val %u\n", i, val); break; } } } } #define DO_PLACED 1 // 1-4 works int main (void) { chan c_but[3]; // Using 6 chanends always par { #if (DO_PLACED == 1) // Works, also with interface. Uses 4 cores, 4 timers, 6 chanends on tile[0].core[0]: handle (c_but); par { on tile[0].core[2]: button (c_but[0]); on tile[0].core[3]: button (c_but[1]); on tile[0].core[4]: button (c_but[2]); } #elif (DO_PLACED == 2) // Works, also with interface. Uses 2 cores, 2 timers, 6 chanends on tile[0].core[0]: handle (c_but); par { on tile[0].core[1]: button (c_but[0]); on tile[0].core[1]: button (c_but[1]); on tile[0].core[1]: button (c_but[2]); } #elif (DO_PLACED == 3) // Works, also with interface. Uses 4 cores, 4 timers, 6 chanends handle (c_but); par { button (c_but[0]); button (c_but[1]); button (c_but[2]); } #elif (DO_PLACED == 4) // Works, also with interface. Uses 2 cores, 2 timers, 6 chanends handle (c_but); [[combine]] par { button (c_but[0]); button (c_but[1]); button (c_but[2]); } #elif (DO_PLACED == 5) // Errs, WORKS with interface on tile[0].core[0]: handle (c_but); // ^~~~~ note: other end is used here par { on tile[0].core[0]: button (c_but[0]); // ^~~~~ error: `c_but' used between two combined tasks on tile[0].core[1]: button (c_but[1]); on tile[0].core[1]: button (c_but[2]); } #elif (DO_PLACED == 6) // Errs, WORKS with interface [[combine]] par { handle (c_but); // ^~~~~ note: other end is used here button (c_but[0]); // ^~~~~ error: `c_but' used between two combined tasks button (c_but[1]); // ^~~~~ error: `c_but' used between two combined tasks button (c_but[2]); // ^~~~~ error: `c_but' used between two combined tasks } #elif (DO_PLACED == 7) // Errs as with interface on tile[0].core[0]: handle (c_but); [[combine]] par { // ^~~~~ error: cannot apply [[combine]] to multi-tile par on tile[0].core[2]: button (c_but[0]); on tile[0].core[3]: button (c_but[1]); on tile[0].core[4]: button (c_but[2]); } #elif (DO_PLACED == 8) // Errs as with interface on tile[0].core[0]: handle (c_but); [[combine]] par { // ^~~~~ error: cannot apply [[combine]] to multi-tile par button (c_but[0]); // ^~~~~ error: components of multi-tile par must have `on' specifier or call a service button (c_but[1]); // ^~~~~ error: components of multi-tile par must have `on' specifier or call a service button (c_but[2]); // ^~~~~ error: components of multi-tile par must have `on' specifier or call a service } #else // warning: unused variable `c_but' [-Wunused-variable] #endif } return 0; } #elif defined TEST_INTERFACE_AND_COMBINE_TEST #include <platform.h> #include <stdio.h> #include <timer.h> // XS1_TIMER_HZ etc #define DEBUG_PRINT_TEST 0 #if (DEBUG_PRINT_TEST == 1) // Uses 1 timer and one chanend (not counted below) #define debug_print(fmt, ...) do \ { if(DEBUG_PRINT_TEST) printf(fmt, __VA_ARGS__); } while (0) #else #define debug_print(fmt, ...) #endif typedef interface button_if_t { void but (int x); } button_if_t; [[combinable]] void button (client interface button_if_t i_but) { timer tmr; int time; tmr :> time; while (1) { select { case tmr when timerafter(time) :> void: { i_but.but(time/XS1_TIMER_KHZ); // ms time += XS1_TIMER_HZ; break; } } } } [[combinable]] void handle (server interface button_if_t i_but[3]) { while (1) { select { case i_but[int i].but (int val) : { debug_print ("handle: from %d val %u\n", i, val); break; } } } } #define DO_PLACED 6 // 1-6 works int main (void) { interface button_if_t i_but[3]; // 6 to zero chanends par { #if (DO_PLACED == 1) // Works, also with chan. Uses 4 cores, 4 timers, 6 chanends on tile[0].core[0]: handle (i_but); par { on tile[0].core[2]: button (i_but[0]); on tile[0].core[3]: button (i_but[1]); on tile[0].core[4]: button (i_but[2]); } #elif (DO_PLACED == 2) // Works, also with chan. Uses 2 cores, 2 timers, 4 chanends on tile[0].core[0]: handle (i_but); par { on tile[0].core[1]: button (i_but[0]); on tile[0].core[1]: button (i_but[1]); on tile[0].core[1]: button (i_but[2]); } #elif (DO_PLACED == 3) // Works, also with chan. Uses 4 cores, 4 timers, 6 chanends handle (i_but); par { button (i_but[0]); button (i_but[1]); button (i_but[2]); } #elif (DO_PLACED == 4) // Works, also with chan. Uses 2 cores, 2 timers, 4 chanends handle (i_but); [[combine]] par { button (i_but[0]); button (i_but[1]); button (i_but[2]); } #elif (DO_PLACED == 5) // Works, NOT with chan. Uses 2 cores, 2 timers, 4 chanends on tile[0].core[0]: handle (i_but); par { on tile[0].core[0]: button (i_but[0]); on tile[0].core[1]: button (i_but[1]); on tile[0].core[1]: button (i_but[2]); } #elif (DO_PLACED == 6) // Works, NOT with chan. Uses 1 core, 1 timer, 0 chanends [[combine]] par { handle (i_but); button (i_but[0]); button (i_but[1]); button (i_but[2]); } #elif (DO_PLACED == 7) // Errs as with chan on tile[0].core[0]: handle (i_but); [[combine]] par { // ^~~~~ error: cannot apply [[combine]] to multi-tile par on tile[0].core[2]: button (i_but[0]); on tile[0].core[3]: button (i_but[1]); on tile[0].core[4]: button (i_but[2]); } #elif (DO_PLACED == 8) // Errs as with chan on tile[0].core[0]: handle (i_but); [[combine]] par { // ^~~~~ error: cannot apply [[combine]] to multi-tile par button (i_but[0]); // ^~~~~ error: components of multi-tile par must have `on' specifier or call a service button (i_but[1]); // ^~~~~ error: components of multi-tile par must have `on' specifier or call a service button (i_but[2]); // ^~~~~ error: components of multi-tile par must have `on' specifier or call a service } #else // warning: unused variable `i_but' [-Wunused-variable] #endif } return 0; } #endif
Code: MAYBE, PASSED WITH CAVEATS and [[guarded]]
Below are four examples of guards and [[compose]]
, the latter seems to be in control of how many chanends
are used. Without further comments, also about the MAYBE
and PASSED WITH CAVEATS
(since I don’t know what they mean). There is some about this at xCore Exchange, but I am no wiser. The below code came to me when I tried to reproduce the Error: lower bound could not be calculated (function is recursive?) messsage above. (I am using time32_t
again here. I like it better). You must comment lines in and out to get CASE1, CASE2, CASE2 and CASE4. xTIMEcomposer 14.3.3:
#include <platform.h> #include <stdio.h> #include <timer.h> // XS1_TIMER_HZ etc. #include <iso646.h> // not, and etc. #define DEBUG_PRINT_TEST 0 #if (DEBUG_PRINT_TEST == 1) // Uses 1 timer and one chanend (not counted below) #define debug_print(fmt, ...) do \ { if(DEBUG_PRINT_TEST) printf(fmt, __VA_ARGS__); } while (0) #else #define debug_print(fmt, ...) #endif typedef enum {false,true} bool; typedef signed int time32_t; typedef interface button_if_t { [[guarded]] void but (int x); //void but (int x); } button_if_t; [[combinable]] void button (client interface button_if_t i_but) { timer tmr; time32_t time; tmr :> time; while (1) { select { case tmr when timerafter(time) :> void: { i_but.but(time/XS1_TIMER_KHZ); // ms time += XS1_TIMER_HZ; break; } } } } [[combinable]] void handle (server interface button_if_t i_but[3]) { bool guard =true; timer tmr; time32_t time; tmr :> time; while (1) { select { case tmr when timerafter(time) :> void: { time += XS1_TIMER_KHZ; // ms guard = not guard; break; } case (guard == true) => i_but[int i].but (int val) : { // case i_but[int i].but (int val) : { debug_print ("handle: from %d val %u\n", i, val); break; } } } } int main (void) { interface button_if_t i_but[3]; // 6 to zero chanends [[combine]] par { handle (i_but); button (i_but[0]); button (i_but[1]); button (i_but[2]); } return 0; } /* CASE1: Guarded and [[combine]] Constraint check for tile[0]: Cores available: 8, used: 1+. MAYBE Timers available: 10, used: 1+. MAYBE Chanends available: 32, used: 0+. MAYBE Memory available: 65536, used: 2796+. MAYBE (Stack: 548+, Code: 2024, Data: 224) Constraints checks PASSED WITH CAVEATS Run-time crash: tile[0] core[0] (Suspended: Signal 'ET_ILLEGAL_RESOURCE' received. Description: Resource exception.) 5188 __interface_yield_once() 0x00010998 5187 _i.button_if_t.handle._c0.but() main.xc:962 0x00010152 ... Lost of lines removed 5091 _i.button_if_t.handle._c0.but() main.xc:962 0x00010152 5090 button.select.yield.case.0() main.xc:941 0x00010264 CASE2: Guarded but no [[combine]] Constraint check for tile[0]: Cores available: 8, used: 4 . OKAY Timers available: 10, used: 4 . OKAY Chanends available: 32, used: 6 . OKAY Memory available: 65536, used: 1796 . OKAY (Stack: 504, Code: 1010, Data: 282) Constraints checks PASSED. RUNS! CASE3: Not guarded and no [[combine]] Constraint check for tile[0]: Cores available: 8, used: 4 . OKAY Timers available: 10, used: 4 . OKAY Chanends available: 32, used: 6 . OKAY Memory available: 65536, used: 1468 . OKAY (Stack: 372, Code: 882, Data: 214) Constraints checks PASSED. RUNS! CASE4: Not guarded but [[combine]] Constraint check for tile[0]: Cores available: 8, used: 1 . OKAY Timers available: 10, used: 1 . OKAY Chanends available: 32, used: 0 . OKAY Memory available: 65536, used: 1820 . OKAY (Stack: 388, Code: 1208, Data: 224) Constraints checks PASSED. RUNS! */
[[distribute]] and [[distributable]]
Observe that these are synonymous terms:
channels cannot be used between distributed tasks ==
channels cannot be used between tasks on the same core, since distributed tasks indeed run on the same core. Just like combined tasks.
More on [[distribute]] and [[distributable]]
[[distributable]]
a task must satisfy being [[combinable]]
first, then all timerafter
and pinsneq
etc. must be removed so that all the components of the while select case
will be defined in some interface
. I guess, you won’t «remove» anything since you’ll have to do this by design: try avoiding spreading timerafter
or pinsneq
all around. Put them in tasks specialised with those needs and leave them [[combinable]]
.
In [23] it’s all defined by XMOS. (It was dsteinwe at the xCore Exchange who pointed to this note for the first time, at Code example for article on 10Aug2020). In that note it’s all so clear:
«If a task is a never-ending loop containing a single select (like a combinable function) that only has cases responding to interface messages, the function can be marked as distributable. .. A distributable task can be distributed within a par. This means that the task will not run on any particular core but will be run on the core of the task that calls to it. .. A distributed task must be on the same tile as the tasks it is connected to.» [23]
⚡Therefore a [[distributable]]
task cannot use chan
/ chanend
!!
So I think that being [[distributable]]
is more useful than being [[combinable]]
since the compiler then can do more «flexible placement» of code [1]. A [[distributable]]
function may be spread around the cores on the same tile, since the compiler (or is it mapper?) may take out the atomic runnable parts with state (or if the » [[distributable]]
task is connected to several tasks», make them atomic by using locks) and run them locally instead of running that code on the core where the code would be placed were it [[combinable]]
. Therefore «A distributable task can be implemented very efficiently if all the tasks it connects to are on the same tile.»
This is so advanced that it’s almost a wonder that it works! It so beyond what the competitors do with realtime kernels and ANSI C. I just felt a need for saying it. (As for all and everything: standard disclaimer!)
I haven’t made any special code example here (but search for [[distributable]] in the search field (top) and you will find examples) – as it’s rather obvious if you have read this far. However, if a task is [[distributable]]
then using it within a [[combine]] par
is legal. However, does it mean that the [[distributable]]
is then only treated as a [[combinable]]
? I queried about this at the xCore Exchange forum: Using [[distributable]] task in a [[combine]] par.
Any commen term for combinable and distributable?
I have asked this question on the xCore Exchange forum: Any commen term for combinable and distributable?
XMOS libs: synchronous/blocking when [[distributable]] and asynchronous when [[combinable]]
I have seen that in two of the XMOS xC libraries that I use they coin a [[distributable]]
task as «synchronous» and they coin a [[combinable]]
task as «asynchronous». That’s for the libraries [lib_i2c] (here) and [lib_spi] (here). You should read the descriptions in those documents, as they quite successfully describe the difference between distributed and combined tasks. Thus the sentence in both of the descriptions that «The synchronous API provides blocking operation» would be a quite correct use of that word. Even if «blocking» has some negative connotations, but there XMOS would simply mean to say that this is the option that is blocking: «The synchronous API will block your application until the bus operation is complete. In cases where the application cannot afford to wait for this long the asynchronous API can be used». (See the problem with the blocking word discussed in Not so blocking after all – where I just now updated with a reference to here.)
It is easy to understand that since a distributable task’s cycles run on the calling task’s logical core, that this is considered synchronous. And that the «messages» (interface calls) that set off another task to do its job on its own is considered asynchronous, even if the interface calls themselves are as synchronous as can be. Here are the code in the two libraries and how the XMOS naming convention seems to be (nothing special for distributable/synchronous but with an _async
postfix for the combinable/asynchronous. (But did they forget about i2c_slave_async
?)
[[distributable]] void i2c_master_single_port (..) [[distributable]] void i2c_master (..) [[combinable]] void i2c_master_async_comb (..) [[combinable]] void i2c_slave (..) [[distributable]] void spi_master (..) [[combinable]] void spi_master_async (..)
[[distributed(task,n)]]
It seems like [[distributed (task,n)]]
is meant to tell the compiler that a task is in fact distributed. I don’t get the picture here, so I am not certain what this keyword does. Observe that [[distributed]]
is well described.
The code: [[distributed ...]]
task2
is distributed that the compiler would not have known by itself? Or does the usage tell the compiler to be more strict about distributing task2
? What does param 2 (0) do?
#define DEBUG_PRINT_TEST 0 #if (DEBUG_PRINT_TEST == 1) // Uses 1 timer and one chanend (not counted below) #define debug_print(fmt, ...) do \ { if(DEBUG_PRINT_TEST) printf(fmt, __VA_ARGS__); } while (0) #else #define debug_print(fmt, ...) #endif interface if1 { int f(int x); void finish(void); }; [[distributable]] void task2(server interface if1 i) { debug_print("%s\n","task2 started"); int z = 22; while (1) { select { case i.f(int x) -> int y: y = x + z; debug_print("%s\n","task2 i.f"); break; case i.finish(): debug_print("%s\n","task2 i.finish"); return; } } } void task1(client interface if1 [[distributed(task2, 0)]] c) { debug_print("%s\n","task1 started"); int x = c.f(5); debug_print("%d\n",x); c.finish(); debug_print("%s\n","task1 finished"); } void task1a(client interface if1 [[distributed(task2, 0)]] c) { debug_print("%s\n","task1a started"); task1(c); debug_print("%s\n","task1a finished"); } int main(void) { interface if1 i; par { #define USE_TASK1A 1 #if (USE_TASK1A == 1) task1a(i); // task2 started // task1a started // task1 started // task2 i.f // 27 // task2 i.finish // task1 finished // task1a finished #else task1(i); // task2 started // task1 started // task2 i.f // 27 // task2 i.finish // task1 finished #endif task2(i); } return 0; } /* DEBUG_PRINT_TEST == 0 on startKIT: Constraint check for tile[0]: Cores available: 8, used: 1 . OKAY Timers available: 10, used: 1 . OKAY Chanends available: 32, used: 0 . OKAY Memory available: 65536, used: 1124 . OKAY (Stack: 340, Code: 680, Data: 104) Constraints checks PASSED */
A nested select can save you a [[guarded]]
Observe this. A [[guarded]] select case
is more complex to build code for than a non-[[guarded]]
. Often the boolean guard that you build is some (state == value)
only used for that particular case
, when all the other case
s probably also need to be [[guarded]]
with (state != value)
. This is standard state programming.
But to avoid complexity some times programming state-less is better. The IEC 61508 safety-critical standard explicitly mentions this. A tool for this is nested select
, that xC allows. This means that in a select case
you may start a new select
with its own case
or case
s.
However, bear in mind that in this case the task cannot be [[combinable]]
or used in a [[combine]] par
. I think the reason is that the compiler will not «flatten out» the nested select
. The SPoC (Southampton Portable occam Compiler), as well as the occam compiler proper, in the nineties did this flattening out of both nested ALT
and nested PROC
, so I think it would be possible here has well. But for now xC doesn’t allow this – which is really no problem.
#include <platform.h> #include <stdio.h> #include <timer.h> // XS1_TIMER_HZ etc #define DEBUG_PRINT_TEST 1 #if (DEBUG_PRINT_TEST == 1) // Uses 1 timer and one chanend (not counted below) #define debug_print(fmt, ...) do \ { if(DEBUG_PRINT_TEST) printf(fmt, __VA_ARGS__); } while (0) #else #define debug_print(fmt, ...) #endif typedef signed int time32_t; typedef enum {false, true} bool; typedef interface button_if_t { void but (time32_t time); } button_if_t; typedef interface background_if_t { void trigger (void); // Signalled by client [[clears_notification]] time32_t read (void); // RPC called from client [[notification]] slave void notify (void); // Signalled/notified by slave // [[guarded]] not needed on notify since nested select } background_if_t; [[combinable]] void background ( const unsigned ix_bg, server interface background_if_t i_bg, const time32_t delta_time) { timer tmr; time32_t time; bool triggered = false; int ret_time; tmr :> time; time += delta_time; while (1) { select { case tmr when timerafter(time) :> void: { ret_time = time; time += delta_time; if (triggered) { triggered = false; i_bg.notify(); } else {} } break; case i_bg.trigger() : { triggered = true; } break; case i_bg.read() -> int ret_time_ : { ret_time_ = ret_time; } break; } } } [[combinable]] void button ( const unsigned ix_but, client interface button_if_t i_but, const time32_t delta_time) { unsigned cnt = 0; timer tmr; time32_t time; tmr :> time; time += delta_time; while (1) { select { case tmr when timerafter(time) :> void: { cnt++; i_but.but (time); time += delta_time; } break; } } } #define NUM_BG_TASKS 2 #define NUM_BUT_TASKS 3 typedef struct { time32_t time; unsigned iof_bg_task; } bg_value_set_t; void handle ( server interface button_if_t i_but[NUM_BUT_TASKS], client interface background_if_t i_bg[NUM_BG_TASKS], static const unsigned num_bg) { while (1) { select { case i_but[int ix_but].but (time32_t time_but) : { bg_value_set_t bg_value_set[num_bg]; debug_print ("handle button %d\n", ix_but); for (int ix_bg = 0; ix_bg < num_bg; ix_bg++) { i_bg[ix_bg].trigger(); // Both background tasks triggered } unsigned expect_num = num_bg; while (expect_num > 0) { // Nested select, even in a loop: select { case i_bg[int ix_bg].notify() : { // Wait for both, pick up one at a time bg_value_set[ix_bg].time = i_bg[ix_bg].read(); bg_value_set[ix_bg].iof_bg_task = ix_bg; debug_print (" handled read %u\n", ix_bg); expect_num--; } break; } } } break; } } } // Why does this have to be here? static const time32_t par_but[NUM_BUT_TASKS] = {XS1_TIMER_KHZ, XS1_TIMER_HZ, XS1_TIMER_HZ*2}; static const time32_t par_bg [NUM_BG_TASKS] = {XS1_TIMER_KHZ, XS1_TIMER_HZ}; int main (void) { button_if_t i_but[NUM_BUT_TASKS]; background_if_t i_bg[NUM_BG_TASKS]; par { on tile[0]: { handle (i_but, i_bg, NUM_BG_TASKS); } on tile[0]: { // [[combine]] Will not run properly par { background (0, i_bg[0], par_bg[0]); background (1, i_bg[1], par_bg[1]); button (0, i_but[0], par_but[0]); button (1, i_but[1], par_but[1]); button (2, i_but[2], par_but[2]); } } } return 0; }
The code: A non-nested select version with [[independent_guard]]
select
version of handle
(see previous code fold). I have used [[independent_guard]]
on the select case
s instead of adding [[guarded]]
in the interface
s. The price for that is that the task cannot be [[combinable]]
in this case.
Observe that [[independent_guard]]
may be obsoleted, see xCore Exchange Guards and Guarded
I thought that this would have used more chanend
s, but the nested select
and non-nested select
solution here (at least) were equal! Both used 6 core
s, 6 timer
s and 11 chanend
s. I have thought that a guard adds more synchronisation, but when I think about of, in the CSP schedulers that I have written myself (in C) (like here), I needed no extra states for the guards. If I do use [[guarded]]
in the interface
s and a [[combinable]]
task and remove [[independent_guard]]
I get the same result. I think I’ll query about this at the xCore Exchange forum.
void handle ( server interface button_if_t i_but[NUM_BUT_TASKS], client interface background_if_t i_bg[NUM_BG_TASKS], static const unsigned num_bg) { unsigned expect_num = 0; bg_value_set_t bg_value_set[num_bg]; while (1) { select { [[independent_guard]] // Use [[guarded]] in interface if [[combinable]] task wanted case (expect_num == 0) => i_but[int ix_but].but (time32_t time_but) : { debug_print ("handle button %d\n", ix_but); for (int ix_bg = 0; ix_bg < num_bg; ix_bg++) { i_bg[ix_bg].trigger(); // Both background tasks triggered } expect_num = num_bg; } break; [[independent_guard]] // Use [[guarded]] in interface if [[combinable]] task wanted case (expect_num != 0) => i_bg[int ix_bg].notify() : { // Wait for both, pick up one at a time bg_value_set[ix_bg].time = i_bg[ix_bg].read(); bg_value_set[ix_bg].iof_bg_task = ix_bg; debug_print (" handled read %u\n", ix_bg); expect_num--; } break; } } }
xC supports tasks that run on a machine with shared memory, accessible from all logical threads on a tile. xC is based on (or should I say inspired by) CSP where chan
nels are used as the lowest level of communication. «On top» of this there is interface
with session roles, like server
and client
. The compiler does parallel usage checks for you, so straight xC is admirably safe.
Personally I have not needed to go beyond this. Or «below» this (where C reigns).
However, there are means to share memory. How to is much described in [1].
But also see the xCore Exchange forum thread Ways to share memory, started by mr. infiniteimprobalility with the nice cat avatar. Quoting from it: «Warning: sharing memory can damage your health! Make sure you understand exactly what is going on to avoid latent strange and difficult to solve runtime bugs!» Here are his points, that he supply with examples and quotes:
- Use
unsafe
pointers - Use
[[distributable]]
tasks.
«Thanks to the[[distributable]]
feature of the compiler, the server task doesn’t cost you an extra logical either. The code gets added to each of the client logical cores and the compiler inserts locks to ensure it remains atomic even when distributed..» - Use inline assembly with
GET_SHARED_GLOBAL
andSET_SHARED_GLOBAL
- Use external C
- Use
movable
pointers.
«Movable pointers are restricted (for your health and safety 😉 ) and make the transferance of ownership explicit through use of the move() operator. This is backed up by runtime checks, which will throw an exception if you try to access it when it’s not yours. Nice.»
Ordered select combined with default case
(12Sep2021) I found this piece of code in lib_mic_array
file decimator_interface.xc
, task/ function check_timing
. I combines a default
with an ordered
prgrama, which makes it into a polling task – but it polls the chan
first:
#if DEBUG_MIC_ARRAY #include "xassert.h" // This line was on the top, though.. static void check_timing(streaming chanend c_from_decimator){ unsigned char val; #pragma ordered select { //Note: only checking decimator 0 is fine as if one fails they all fail case sinct_byref(c_from_decimator, val):{ fail("Timing not met: decimators not serviced in time"); break; } default:break; } } #endif
Configuration (mapping) language
This is not about Configuration as defined in the Glossary [11]: «In the xTIMEcomposer tools, Configuration refers to setting values for the properties of an xSOFTip block».
However, it is a about what the par
statement needs to get the code compiled, placed on
a tile
, about what to run on a core
, and about port
pins or units. There also are the [[combine]]
and [[distribute]]
decorations.
Then there is the .xn file, with the low-level stuff defined. It’s described in a document Describe a target platform [18]:
Hardware platforms are described using XN. An XN file provides information to the XMOS compiler toolchain about the target hardware, including XMOS devices, ports, flash memories and oscillators.
The XMOS tools use the XN data to generate a platform-specific header file <platform.h>, and to compile, boot and debug multi-node programs.
The good thing is that the «configuration language» is integrated into xC so we don’t know it’s there. The xn files would typically belong to a particular board. So if you buy XMOS boards, the examples will contain them. Or do they come with the xTIMEcomposer installation? I am not sure. Anyhow, as said above, the <platform.h>
is rather generated for you.
Not as with the occam languge in the nineties where there was a concrete configuration language and even a tool, the occonf to handle it [17]. The reserved words were NODE
, ARC
, EDGE
, NETWORK
, MAPPING
, CONFIG
, SET
, CONNECT
, MAP
, PROCESSOR
, DO
and in the last version also IF
. xC is designed with all that knowledge first hand, and also the experience about what worked and didn’t, with that concept.
My problem is that even if there are so much checking on this by the xC compiler or mapper (there is a great flora in the error messages above), I seem to stumble upon many code examples where errors are wrongly issued or not issued at all. It compiles but won’t run.
I have started several error tickets (issues) to XMOS on this, and they are always there to respond. And some has been discussed on the xCore Exchange forum. And some I just have left as a comment or two in some of the code here and in the xC code examples I think.
I struggle with this. I know this is very complicated. Also for XMOS, I gather. My suggestion is for XMOS look at this with new eyes, not only handle the cases. I have not seen any formal syntax of the language (full + config) as of now. I miss it. Disclaimer: I would not know what XMOS might already do behind the scene. (xTIMEcomposer 14.3.3 in July 2018)
Parallel usage checks
So many of the error messages seen above are about this. This another one of the great benefits of this langguage. Read [1].
Right now I only wanted to mention that declaring a function as const
seems to avoid this check. See where I queried about this at Stealing a log value from a dead task – just search for «parallel usage checks» (11Feb2019)
memcpy or not?
Some times you may have to use memcpy
. It’s in <string.h>
and goes like void *memcpy (void *to, const void *from, sizeof (object or type));
The XMOS Programming Guide [1] mentions memcpy
once. That’s when you may need to «to copy local data to the remote array. This will be converted into an efficient inter-task copy». The example also makes up a one page application note [20].
I have also discussed this somewhat earlier, see Ticket: Returning values in arrays via interfaces.
But beware, xC is a C/Cpp compiler from birth, and it may compile dead rotten code for you with no warnings! Test to get sure it works! (However, I do have some doubt whether the problems I got with compilable code that won’t run correctly is intended compiler behaviour. I use xTIMEcomposer 14.3.3 at the moment (Feb2019))
→ For the rest of this chapter I have not found any root cause of my persistent problem. It may be an interesting read when I have found out of it, but for now it’s out of scope. Or rather, I use my new scope and/or the xTIMEcomposer xSCOPE find out more. That’s including the code example:
This happened to me when I was passing {two, val}
as a parameter list out of a function, not an interface
. The data I was going to return-only was being produced inside the function as a field-far-way-down-in-a-struct. I tried just to assign that field to the return field. It failed and I got no warning. I tried memcpy
and was happy to see the odd error: incompatible type for argument (when I did not decorate the reference) or error: indirection requires a pointer argument (when I used a pointer typecast *
instead of a reference typecast &
).
I gave up and decided move the two return
params to the list after the function. I thought that since I was going to get data out of the function then it needed to be a reference like &two
, &val
. I tried with a memcpy
for each, decorated with &
; but it failed. It also failed if I just tried to assign them to each other. Perhaps fair enough since I did list up two references in my param list(?), and the memcpy
or assignment didn’t move data out. Observe that for each of the cases that failed I got no errors from the compiler.
I then gave up again but wanted to try a final case. I removed the &
before two
, val
. In my head that should have been a value assignment into the function, but since I had not decorated the param with const
the field (as we shall see) certainly was passed out of the function. I then just assigned the values to each other and it worked! Also, if I did memcpy
it also worked! Here is the code that worked (WORKS 1
or WORKS 2
), taken out of the blue for you I’m sorry. I ended up with this case where I did not need to use a memcpy
:
The code: memcpy or not?
This code is taken from a pattern I have developed that will be covered in a separate note (in Feb-Mar2019). It avoids using an interface call altogether if a task has been livelocked and will not be able to treat any interface call after this. It would pathologically block the caller. Right now only the red text is in scope I guess:
UPDATE 20Feb2019:
there shall of course be &refparams on the two red params! Stay tuned! (With WORKS==1 it works on the xCORE-200 at least, with &refparams)
interruptAndParsingResult_e handleSPIInterrupt_iff_asynch ( client radio_if_t i_radio, timing_transx_t &session_trans, some_rfm69_internals_t return_some_rfm69_internals, // No &refparam here packet_t return_PACKET) // No &refparam here { interruptAndParsingResult_e return_interruptAndParsingResult; if (not session_trans.timed_out_trans1to2) { session_trans.start_time_trans1 = i_radio.handleSPIInterrupt_trans1 (); { return_trans3_t return_trans3; do_sessions_trans2to3 (i_radio, session_trans, return_trans3); // Will detect timed_out_trans1to2 #define WORKS 2 #if (WORKS==1) return_some_rfm69_internals = return_trans3.u_out.handleSPIInterrupt.return_some_rfm69_internals; return_PACKET = return_trans3.u_out.handleSPIInterrupt.return_PACKET; #elif (WORKS==2) memcpy (&return_some_rfm69_internals, &return_trans3.u_out.handleSPIInterrupt.return_some_rfm69_internals, sizeof (some_rfm69_internals_t)); // no decoration: error: incompatible type for argument // * error: indirection requires a pointer argument memcpy (&return_PACKET, &return_trans3.u_out.handleSPIInterrupt.return_PACKET, sizeof (packet_t)); #else #error #endif return_interruptAndParsingResult = return_trans3.u_out.handleSPIInterrupt.return_interruptAndParsingResult; } } else { // Server may be pathologically blocked, don't use i_radio } return return_interruptAndParsingResult; // Only valid if not timed out }
Priority
xC language
Tasks cannot be prioritised in xC. I assume that this would not make much sense on this architecture (either). (Some times they don’t.) Logical threads have their cycles. I guess that prioritising one of them might also make deterministic timing analysis and guarantees difficult.
Threads XS1 architecture
I don’t think the XS1 architecture has high priority threads. In an XS1 data sheet ([31]) I read on page 14 that:
Tasks do not need to be prioritised as each of them runs on their own logical xCORE. It is possible to share a set of low priority tasks on a single core using cooperative multitasking.
I assume that they here refer to combinable / combined tasks as «low priority tasks». This makes sense. And of course, distributable / distributes tasks have no own will, so they would also be «low priority».
Threads XS2 architecture
The xCORE-200 architecture, though, contains priority threads. In [30] (XS2 architecture) I read on page 14:
There is no way that the performance of a logical core can be reduced below these predicted levels (unless priority threads are used: in this case the guaranteed minimum performance is computed based on the number of priority threads as defined in the architecture manual). Because cores may be delayed on I/O, however, their unused processing cycles can be taken by other cores. This means that for more than five logical cores, the performance of each core is often higher than the predicted minimum but cannot be guaranteed.
…
(Five because the instruction pipeline is 5 deep on the XS2, but 4 on the XS1). Then the same text as for the XS1 quote above.
In [29] (an XS2 data sheet) I read that «the use of priority threads will cause a slightly different but still predictable performance pattern«. Then, chapter 5.3 goes like this:
«Threads can be set to be high priority. If no high priority threads are runnable, then a low priority thread will be scheduled if one is runnable. If high priority threads are runnable, then they will be scheduled, but at least one low priority thread will be executed on every iteration of the high priority queue. This means that all threads are always guaranteed progress.
Threads start as low-priority and only threads that require a very short turn around time or maximum throughput will be high priority.»
I assume that this would be for assembly programming, since I have discovered no feature of xC that supports thread priority.
select construct of xC
I have been to many conferences about this type of architecture (WoTUG and CPA conferences, here) and there seems to be some kind of agreement that it’s not really tasks that need priority, it’s the messages. I therefore find it rather nice that xC can do this, with [[ordered]]
. See [1] for description, but it’s basically like this:
[[ordered]] // OBS in standard task type only, not in [[combinable]] or [[distributable]] task select { case A: { // ... } break; case B: { // ... } break; case C: { // ... } break; }
The occam PRI ALT
for the transputer was implemented as a standard non-prioritised ALT
, as far as I know. I don’t know how xC [[ordered]]
is implemented as compared to not ordered. To find out more about «fairness» then just search for [[ordered]]
in this document.
port construct of xC
However, in the description of ports where one might have overlapping mention of them, some may be mentioned both as 4, 8, 16 and 32 bit ports. Then the lower bit description takes precedence or «priority». In [12] chapter «49XS1 port-to-pin mapping» there is this example, where «the narrower port takes priority. For example:»
on tile[2] : out port p1 = XS1_PORT_32A; on tile[2] : out port p2 = XS1_PORT_8B; on tile[2] : out port p3 = XS1_PORT_4C;
Chanend in select
This is perfectly fine:
void select_chanend_task ( chanend chan_0, chanend chan_array[3]) { unsigned new_value; unsigned value = 0; unsigned value_array[3] = {1,2,3}; while (1) { select { case chan_0 :> new_value : { value += new_value; chan_0 <: value; } break; case chan_array[unsigned ix] :> value_array[ix] : { chan_array[ix] <: (value + value_array[ix]); } break; } } }
Observe the very interesting example of a select function in [1], page 43. It shows the usage of a chanend together with an unsigned timeout value.
Select
In summary, these (potentially guarded) commands are possible. Plus, they may come in arrays as well. See above chapter.
select { case "interface call" case "chanend" case "select function" (see end of above chapter) case "port" case "timout" }
Observe the use of expressions for [[guarded]]
handling of select guards. In Wikipedia (on occam, here) it’s explained like this. I have replaced ALT
with select
:
«select specifies a list of guarded commands. The guards are a combination of a boolean condition and an input expression. Each guard for which the condition is true and the input channel is ready is successful. One of the successful alternatives is selected for execution.»
Synchronous, or asynchronous with streaming or buffered
A chan
is one-to-one, synchronous and blocking (aside: but Not so blocking after all). This is like the channels of CSP and occam. Observe that we have shown how the use of a chanend
at each task may be used for bidirectional communication, provided that the tasks are hard coded to agree on when to switch back and forth.
Then we have streaming chan
. From [1]:
«Streaming channels allow asynchronous communication between tasks; they exploit any buffering in the communication frabric of the hardware to provide a simple short-length FIFO between tasks. The amount of buffering is hardware dependent but is typically one or two words of data. Channels (both streaming and normal) are the lowest level of abstraction to the communication hardware available in xC and can be used to implement quite efficient inter-core communication but have no type-checking and cannot be used between tasks on the same core.»
This is unlike a go chan, which is buffered with a known number of elements, called capacity (0 for blocking, synchronous unbuffered) like make(chan int, capacity)
. But then, «you don’t need them» (Rob Pike in Pike & Sutter: Concurrency vs. Concurrency). Plus, if you build your task relying on a buffered chan never to block, you are asking for trouble. It blocks when it’s full. This can happen a year after shipment of the SW, when the system freezes and you have to update 300k unit’s SW.
The go chan
is typed, but the xC chan is typeless (but both parts have to agree).
Then we have xC port
and buffered port
. Also from [1]:
XMOS devices provide buffers that can improve the performance of programs that perform I/O on clocked ports. A buffer can hold data output by the processor until the next falling edge of the port’s clock, allowing the processor to execute other instructions during this time. It can also store data sampled by a port until the processor is ready to input it. Using buffers, a single thread can perform I/O on multiple ports in parallel.
Then, the basic xC interface
is built by synchronous «remote procedure calls», treated as «events» on the receiver side. But there is the asynchronous pattern that we get with notifications, shown with [[notification]]
and [[clears_notification]]
. (Aside: This certainly reminds me of a blog note I wrote in 2009: 009 – The «knock-come» deadlock free pattern.)
Of course, inserting a third task that does nothing else than to buffer data is also a means to decouple tasks, often used at the edges of the processor. If one in addition needs a controlled means to lose data, then there is the overflow buffer task that you have to build yourself. A buffered chan
does not have this possibility. An buffer task is a buffered chan
plus individual processing of each element (Aside: overflow buffer, which I have discusse in 072:[12].)
Off-piste xC code examples
Other, more standard code examples, see xC code examples
Chan syntax confusion
How many syntactical combinations could we possible have for the same semantics? Here are the ones I have had to relate to over the years. Channels are named «ch»:
occam | ch ! value // output |
Two single-letter tokens (direction), ch left |
go/golang | ch <- value // output |
One two-letter token, same way, ch sided (direction) |
xC | ch <: value; // output |
Two two-letter tokens (direction), different ways, ch left.
Observe that xC also uses |
Using a chanend in both directions
Bidirectional channels! Rather unusual xC. Only chan
. No use of interface
. No select
! Still, quite possible. Give and take it’s almost occam. Press to open code in text fold:
The code: Using a chanend in both directions
void My_Server (chanend c_in, chanend c_out) { int value; while(1) { c_in :> value; // Input debug_printf (" My_Server _ %u\n", value); value++; c_out <: value; // Output debug_printf (" My_Server X %u\n", value); } } void My_PingPong (chanend c_in_out) { int value; while(1) { c_in_out :> value; // Input debug_printf (" My_PingPong _ %u\n", value); value++; c_in_out <: value; // Output debug_printf (" My_PingPong Y %u\n", value); } } void My_Client (chanend c_out, chanend c_in, chanend c_out_in) { int value = 0; while(1) { debug_printf ("My_Client X %u\n", value); c_out <: value; // Output c_in :> value; // Input debug_printf (" My_Client Y %u\n", value); c_out_in <: value; // Output c_out_in :> value; // Input (Change dir: deadlock caught by run-time) debug_printf (" My_Client Z %u\n", value); } } int main(void) { chan c_over; chan c_back; chan c_pingpong; par { My_Server (c_over, c_back); My_PingPong (c_pingpong); My_Client (c_over, c_back, c_pingpong); } return 0; } /* Simulator (same if I run with startKIT HW) My_Client X 0 My_Server _ 0 My_Client Y 1 My_Server X 1 My_PingPong _ 1 My_Client Z 2 My_PingPong Y 2 My_Client X 2 My_Server _ 2 My_Client Y 3 My_Server X 3 My_PingPong _ 3 My_Client Z 4 My_PingPong Y 4 My_Client X 4 My_Server _ 4 My_Client Y 5 My_Server X 5 My_PingPong _ 5 My_Client Z 6 My_PingPong Y 6 Constraint check for tile[0]: Cores available: 8, used: 3 . OKAY Timers available: 10, used: 3 . OKAY Chanends available: 32, used: 6 . OKAY Memory available: 65536, used: 12716 . OKAY (Stack: 4608, Code: 7314, Data: 794) Constraints checks PASSED. Build Complete */
As you see by the log, it works: a chanend
may be used in any direction. If I build this code with two chanend
s (instead of one as in the example) for My_PingPong
then I get the exact same behaviour as with one chanend
. Of course it would add up to two more chanend
s used, one at each end of the channel. But bear in mind that the actual usage is always important to avoid deadlocks, no less with one chanend
than with two. Each end has to agree on the direction of the next transmission. The compiler will not give any error message on wrong usage.
If I can save two chanends by «reuse», why doesn’t the tool do this automatically? When I do a clean ping-pong type sequence from a client to a server and back, like in the example, and I had declared a channel in each direction, the tool could have optimised and used only one channel for it? And saved two chanend
s.
However, if I change direction on the last input in My_Client
to do an output instead then the deadlock then it is caught at run-time (this may also happen if I type direction wrongly in some standard usage example) :
tile[0] core[1] (Suspended: Signal 'ET_ILLEGAL_RESOURCE' received. Description: Resource exception.) 3 My_PingPong() xc_test_2.xc:38 0x00010104 2 main_task_My_PingPong_1() xc_test_2.xc:63 0x000101b8 1 __start_core() 0x00010fb2
In other words, a chanend
is bidirectional, just like a chan
in go/golang and a rendezvous in Ada. And, I should say, CSP, since it’s only a shared state where all parts wait standing still, and data may be moved in any direction at the synchronisation point. (It’s the implementation of the ALT
in occam, and it being a multi-core distributed memory architecture that didn’t make occam’s chan
bidirectional. And didn’t open for output guards.) A channel is a permission to do anything with some data that, at the synchronisation point, is shared. Also see Calculating number of chanends
The log above also is an exercise in how the scheduling is done. At first sight it may look wrong, but value is incremented and a complete round is not compromised. It works.
I have shown this code at XMOS xCore discussion forum here. Interesting response there!
Replicated select case
(Also search for «replicated select case» in the present note. Plus 035:[xC on XCore by XMOS])
occam
has replicated ALT
. For a long time I though that xC didn’t have replicated case
in select
. It does, for «resources» (below). (But for chan
arrays? TODO: Test!)
Until I discovered some code in lib_i2c
, file i2c_master_single_port.xc
:. I
void i2c_master_single_port(..) { while (1) { select { case(size_t i =0; i < n; i++) (n ==1||(locked_client ==-1|| i == locked_client))=> c[i].read(uint8_t device,uint8_tbuf[m],size_t m, int send_stop_bit)->i2c_res_t result: { // .. } break; // .. } // select } // while } // i2c_master_single_port
Then I also found it in [1] in chapter 4.4 «Replicated cases» p41: «Replicated cases iterate the same case several times. This is useful if the program has an array of resources to react to. For example, the following code iterates over an array of timers and associated timeouts.» On p46 resources are mentioned to be: «Resources in xC such as interfaces, chanends, ports and clocks, must always have a valid value. »
Aside: observe also the other select
construcs, like select
functions.
Remember to set the return value of an interface function!
I discovered this in Nov2017 and sent it to XMOS. Within the hour they had come up with a much better code example that mine. I have been allowed to show theirs here. It’s also possible to write compact client/server code in xC. I added the «server task» and «client task» comments, but even if the reserved words server
and client
are not used in this code, that’s basically how we should think about the two par
components here:
01 interface i { int f(void); }; 02 int main(void) { 03 interface i i; 04 par { // server task: 05 while (1) { 06 select { 07 case i.f(void) -> int x: 08 //x = 123; 09 break; 10 } 11 } 12 { // client task: 13 printf("Value returned is %d\n", i.f()); 14 exit(0); 15 } 16 } 17 return 0; 19 }
The point is that the rubbish value not in x
from line 09 isn’t caught by the compiler. So the client in line 13-14 would print rubbish.
XMOS says that this is a «known limitation» in the xC compiler. Fair enough, I’d have to take care. XMOS continues that the issue is tracked and that there may be an update.
However, I do see that to make it 100% the compiler would have to follow the trace and see that it is correctly returned in all cases, provided there are some conditional statements in the interface
function. My gut feeling is that the compiler does a lot of static analysis, so this might perhaps be possible. And when the xC language designers have decided to not have a return
statement in interface
functions (which is quite confusing because it breaks C type style, I’d like to know why), then they can’t hide behind a missing return
statement message. But in C I’d still have the same problem: present return
statement but not updated returned value is still not detected. But I’d like xC to be better the C also on this point! But for C I would think that lint would detect a return value with not updated value. So I need the xC-lint to be built-in!
Thinking about it, it’s nice not to have a return
statement too. Here’s some code from my Aquarium code (further below). Here I return values all over the place. In the ret_thirds
(as an array) and differently the values ret_light
and ret_control
. It’s nice to be able to be freed of any return
statement here, as it won’t add anything at all. Code in pink or red where I build return values. This is just an extract, and I had to tune names to get them into this window (I normally use rather descriptive names), so take it with a grain of salt.
<aside> Operators like bitand
have been in <iso646.h> to make code less error prone and more readable since the beginning of time. I guess that’s why C programmers detest it; I mean that it’s words like and
and bitand
and not cryptic &&
and &
, of course only kept safe by the high priests. I must admit, to my agony, I had to look it up now to be really sure – but then I started with C only 15 years ago. And spoiled it with iso646.h. So I could never become no C high priest. But this is something that people with experience from Modula-2 just love. Or with occam experience, where and bit operators, boolean and and bitwise and are precisely defined and easy to distinguish. But for the last, David May, the main designer of both occam and xC let that go to make xC look more like C. Alas, probably a good idea. Some people never stopped complaining of occam syntax at the conferences. But then came Python with indentation, too.. </aside-forget-it>
case i_port_heat_light_commands[int index_of_client].get_light_etc (unsigned ret_thirds [NUM_LED_STRIPS]) -> {light_t ret_light, control_t ret_control} : { for (unsigned iof_LED_strip=0; iof_LED_strip < NUM_LED_STRIPS; iof_LED_strip++) { ret_thirds [iof_LED_strip] = 0; // Clear first.. } for (unsigned iof_pwm=0; iof_pwm < NUM_LED_STRIPS; iof_pwm++) { unsigned int mask = pwm_windows[iof_light_level_present][iof_pwm]; if ((mask bitand BIT_LIGHT_FRONT) != 0) ret_thirds[IOF_LED_STRIP_FRONT] += 1; // ..then conditionally increment if ((mask bitand BIT_LIGHT_CENTER) != 0) ret_thirds[IOF_LED_STRIP_CENTER] += 1; // ..then conditionally increment if ((mask bitand BIT_LIGHT_BACK) != 0) ret_thirds[IOF_LED_STRIP_BACK] += 1; // ..then conditionally increment } ret_light = light; ret_control = control; } break;
So here, if I didn’t have the pink code the xC compiler should have issued an error message. And if the pink code were present, I’d like to be caught if the for
-loop were too short and this did not include all elements of ret_thirds
. And of course, if any of the two last lines were not present.
I am using it like this on one of the clients:
{context.light, context.control} = i_port_heat_light_commands.get_light_etc (context.light_intensity_thirds);
Running xTIMEcomposer functions from command line
I run OS X/macOS.
XFLASH from Terminal
Updated 7Dec2018: xTIMEcomposer 14.3.3 (as of April 2018) requires the old Java 6 (JRE) runtime. So when you try to run xTIMEcomposer you’re told it needs this older JRE (here). Just download and install and xTIMEcomposer is soon running fine. I tried this for all OS X / maxOS versions since OS X El Capitan 10.11.6 (Sierra 10.12, High Sierra 10.13 and Mojave 10.14.2 (Dec2018)). All seems fine except the fact that you’ll have to FLASH in a Terminal window since xTIMEcomposer gets an internal error if you try it from there. More about this here. I have kept my XMOS work-machine at OS X El Capitan 10.11.6 since FLASHing works fine in xTIMEcomposer 14.3.3 there, but I will now update also that machine now years behind because of this. Anyhow, here’s how to do FLASHing:
- Have a look at XFLASH Command-Line Manual [35] first
- Terminal: open an instance
- Finder: open /Applications/XMOS_xTIMEcomposer_Community_14.3.3/ (or any other version)
- Update 19Feb2023: For an older MacBook where I only wanted to run xflash it wasn’t as easy as this. It runs macOS Catalina 10.15.7. (Aside: I failed on macOS Monterey 12.6.2 because the developer could not be verified.) Catalina keeps telling me that
The default interactive shell is now zsh.
To update your account to use zsh, please run `chsh -s /bin/zsh`.
For more details, please visit https://support.apple.com/kb/HT208050.
So I ranchsh -s /bin/zsh
and logged in. It replied withchsh: no changes made
- Update 19Feb2023: For an older MacBook where I only wanted to run xflash it wasn’t as easy as this. It runs macOS Catalina 10.15.7. (Aside: I failed on macOS Monterey 12.6.2 because the developer could not be verified.) Catalina keeps telling me that
- Drag the
SetEnv.command
into the Terminal window and hit enter- I managed if I did
cd /applications
then
cd XMOS_xTIMEcomposer_Community_14.4.1
to to get there.
Then./SetEnv.command
succeeded. Observe the preceeding./
But I don’t know why is keeps nagging with the lines starting withThe default interactive shell
- I managed if I did
- Finder: open your xTIMEcomposer workspace and find the bin directory
- Make sure the project is properly built with the xTIMEcomposer IDE
- Make sure the XMOS board is connected to your Mac. The startKIT needs no extras, just a USB cable. The xCORE-200 eXplorerKIT needs an XTAG-3 (XTAG3) board as well. If not you’ll get Error: F03136 (see above)
- At this stage it’s probably easiest to have only one USB cable connected to any XMOS board, the one you’re programming. If not you must retrive a correct adapter ID. You’ll find all this in the xTIMEcomposer User Guide
xflash -l
bash-3.2$ xflash -l Available XMOS Devices ---------------------- ID Name Adapter ID Devices -- ---- ---------- ------- 0 XMOS startKIT 0zGcIk5WkhKBj L[0] 1 XMOS XTAG-3 AV4Xa6rG O[0]
Observe that this command does not tell whether any XTAG-3 is connected to an xCORE-200 eXplorerKIT board (or whatever). If not connected I got a
xrun: Binary (xs2_reva) does not match architecture (xs1_revb)
. Strange message for this, but true. This would then mean that you would need to add the xflash parameter (like:) –id AV4Xa6rG- I also may get
Cannot connect, device is in use by another process
it the Debugger is already running - Also works for the blue indented case here
- Type xflash in the Terminal window and drag and drop the .xe file you found in the .bin directory. Hit enter. This would run something like
xflash /Users/teig/workspace/_Aquarium_1_x/bin/_aquarium_1_x.xe- Also works for the blue indented case here.
- However, if I tried to reach the file from iCloud, the local copy that macOS keeps on the local machine failed.
xflash
complained that
input file for --factory not found
- However, if I tried to reach the file from iCloud, the local copy that macOS keeps on the local machine failed.
- Also works for the blue indented case here.
- There wil be some logging (some of it only appears in the window titlle) and a finishing message:
Site 0 has finished successfully
.- Also succeeds for the blue indented case here
Debugging two different boards: two xTIMEcomposers
I needed to simultaneously run/debug one startKIT board and one xCORE-200 eXplorerKIT board with the same project / source files – with only a little difference between the two (controlled with #ifdefs). It is actually possible! Not ideal, but you don’t need another machine: Thanks for help in this xCore Exchange thread: Debugging two different board
- Make a copy of your workspace. I called my workspace duplicate workspace2
- Terminal: open an instance. Don’t change directory. If you do xTIMEcomposer will start but not find your files or system files
- Also, don’t try to use this Terminal window for anything else since that would terminate xTIMEcomposer (said from a low grade Linux user)
- Finder: find your xTIMEcomposer applications directory («Show in Finder» on the icon) and find the xtimecomposer_bin/xtimecomposer.app (then «open package») /contents/MacOS/xtimecomposer file and push xtimecomposer.command into Terminal and hit enter. This contains SetEnv.sh so that’s done for you.
- Alternatively you could paste this (or the like) into Terminal (I use default installation):
/Applications/XMOS_xTIMEcomposer_Community_14.3.3/xtimecomposer_bin/xtimecomposer.app/Contents/MacOS/xtimecomposer.command
and hit enter
- Alternatively you could paste this (or the like) into Terminal (I use default installation):
- This starts the second xTIMEcomposer IDE for you! They seem to be completely independent.
- The two instances of xTIMEcomposer seem to share some info, so it’s a good idea to untick «Use this as the default and do not ask again» in the Workspace launcher window. For me I can the select workspace or workspace2
- When I upgraded from 14.3.2 to 14.3.3 (Apr2018) I at first had to do the online sign-in in the xTIMEcomposer Studio Registration dialogue box for the one instance. But I also had to do it a second time for the second instance. See Double xTIMEcomposer Studio Registration on xCore Exchange where andrew responds that «The login is to allow us to know which versions of the tools are active so we can know how far back we need to maintain support for. If no one is using old versions, after a while, we could choose to deprecate them.» He did not tell why I needed to log into both instances
- When I make changes in the workspace and/or workspace2 I use ChronoSync to help me synchronise them. I set up a Synchronizer and do it more or less by hand in the Analyze tab. I don’t do Git on workspace2, only in workspace. But I do a Commit there first, then the Synchronize with ChronoSync, then a Merge afterwards. But the easiest is not to touch the code in workspace while I work with workspace2 and need to edit there, then the Commit and Merge are not needed. Only a Synchronize and then a Refresh in workspace. Er just edit in workspace and copy/paste into workspace1 with Finder
- I saw that I went into some problem and then the original icon running with workspace thought that it controlled the one with workspace2. So I had to restart both and start the one wth workspace first
Other
- Note Channels and rendezvous vs. safety-critical systems (035), chapter xC on xCore by XMOS
- Note Nondeterminism (049,) chapter xC by XMOS
- Note My XMOS notes (098)
Porting to XMOS / xC
I have a chapter with the same heading in My aquarium’s data radioed through the shelf. It’s about how to attack Arduino code and try to get it run on XMOS with xC, C and Cpp.
Data types long long, float, double and long double
These are supported! See Data types long long, float, double and long double
(For search: REAL, REAL32, REAL64)
Inserting run-time checks
There is an interesting overview of this by robertxmos, at http://www.xcore.com/viewtopic.php?f=26&t=6335&p=31628#p31628. It’s worth a read
Architecture defines
I guess this is what I think I know now:
- __xC__ is defined by the xCC compiler when an .xe file is compiled
- __XS1B__ is defined by the compiler if TARGET = STARTKIT. You can set it in the Makefile, but I think that for a new project it’s set automatically if a startKIT is plugged in
- __XS2A__ is defined by the compiler if TARGET = xCORE-200-EXPLORER
- Inside XMOS_xTIMEcomposer_Community_14.3.2 in
/target/include/
I find these files with «registers» in the name. However, I can’t find __XS1A__ in them, and two seem to be of other targets:- xs1_g_registers.h – uses __XS1_G__ (which board/target)?
- xs1_l_registers.h – uses __XS1_L__ (which board/target)?
- xs1_registers.h i – uses __XS1B__ and __XS2A__
- xs1_su_registers.h – uses no __..__
- xs1b_registers.h – uses no __..__
- xs2_su_registers.h – uses no __..__
- xs2a_registers.h – uses no __..__
I guess that [13] may also help some.
But you can find out more from the xCore Exchange post start I started __XS1_L__ __XS1B__ __XS2A__ architecture defines (Feb2018). From it we have this:
The xC compiler’s inbuilt defines may be displayed running in a Terminal window in the directory where xcc1llvm
live: (The ref above also shows how to get these from the C and C++ compilers). The ./
was at least needed for OS X:
./xcc1llvm -dM -E /dev/null -march=xs2a -march=xs1b (the -march option is optional, defaulting to xs1b), shows __XS1B__ as defined (for, like TARGET = STARKIT) ./xcc1llvm -dM -E /dev/null -march=xs2a -march=xs2a shows __XS2A__ as defined (for, like TARGET = xCORE-200-EXPLORER)
There also is one defined __XS1_L__
that’s used in some code that uses an asynchronous ring oscillator giving an independent «truly random» value.» (?) See the XMOS [module_random]
file random_init.c
. But I don’t think this is in the .march
group.
Including interface functions in local .xc file or library
See Including interface functions in local .xc file or library in the xCore Exchange. The conclusion is that the linker shall not link unused interface functions. It did in my case, so this seems to be a tool issue.
nullable types, null and NULL
C.A.R. Hoare invented the null reference or null pointer and he called it his «billion-dollar mistake» [19]. Still we have it with us:
#define NULL ((void*)0)
It’s defined in <stdlib.h>
if you need it for something else than what this chapter is about.
But then, xC has a keyword null
. This is used when a nullable parameter is nulled. Example:
nullable types, null and NULL (source code) library?
USE_S
defines if we actually use an object (=1). And if we don’t (=0) then we can get the error with WORKS
(=0) or OK with WORKS
(=1):
// #include <xs1.h> // Not needed #include <stdlib.h> // Defines NULL not correct to use here void fr (int &?y) { // XMOS Programming Guide, XM004440A p47 if (!isnull(y)) { // We know y is not null so can use it here } } void fa (int (&?a)[5]) { // XMOS Programming Guide, XM004440A p47 if (!isnull(a)) { // We know a is not null so can use it here } } typedef struct s_t { int val; } s_t; #define USE_S 0 #define WORKS 1 // 0 or 1 valid if USE_S is 0 void fs (s_t &?s) { #if (USE_S==1) // We know s is not null so can use it here #else // s is always valid #endif } int main() { int y; fr (y); fr (null); int a[5]; fa (a); fa (null); #if (USE_S==1) s_t s; // Only defined when needed #define MY_S s #else #undef NULL #if (WORKS==1) #define MY_S null #else #define NULL ((void*)0) // Same as in "stdlib.h". Same problem: #define MY_S NULL // "error: invalid initialization of reference for argument" #endif #endif fs (MY_S); // s(ok), null(ok) or NULL(err) return 0; }
My «problem» is that I have some large chunk of code where I have happily used NULL
as ((void*)0)
and it compiles and runs correctly, without the error message error: invalid initialization of reference for argument … I have not been able to recreate that situation in the code below, though. I posted a query about this in the xCore Exchange here.
Not used parameter not set up in call
I had an interface
defined as a parameter in a function. There were also several other interface
parameters and other parameters. I discovered that one interface
parameter was not used. The compiler (14.3.3, compiled 25Jul2019) did not warn about this. It usually warns about unused variables. However, when I removed the interface
from the parameter list, and removed the interface
parameter from the calls, nothing happened with the used memory. It was the same, implying that the compiler did not set up that parameter in the first place. I like it.
However, when i tried this at another place I got 8 more bytes used when I removed an interface
parameter to a function called twice. Optimization some times takes its own path I guess.
xCore instruction set
The assembly (for searching: assembler) instructions are described the The assembly Programming manual on pp 302 and in that chapter Instructions on pp 318 of [12] (14.0.x), which by the way contains everything you need to know! The assembly program syntax is shown on page 326. Instructions are grouped:
- Data Access
- Branching, Jumping and Calling
- Data Manipulation
- Concurrency and Thread Synchronization
- Communication
- Resource Operations
- Event Handling
- Interrupts, Exceptions and Kernel Calls
- Debugging
- Pseudo Instructions
PWM examples
PWM = Pulse width modulation
- The light control of my aquarium SW, see My aquarium notes (Jul2018 to now)
- Softblinking LEDs, see My xC softblinking PWM notes (Jul2020)
Chan or interface when critical timing
(10Sep2021) Observe the different semantics for in interface
call and chan
communication for a time critical system. In this case I had to use chan
:
See 219:[Chan or interface when critical timing]
XMOS libraries
Overview
Also see 243:[XMOS libraries].
Update 3May2022. It looks to me like some libraries are newest at (1) https://www.xmos.com/libraries/ and some are newest at (2) https://github.com/xmos/. However (1) seems to set up urls to repos of some zip’ed git version at (2). Also beware that at some point xTIMEcomposer (14.4.1) buildable version versions end and XTC Tools versions start. It’s not just to «pip» in the last version. (Disclaimer: it might be from XTC tools on VSCode.)
I may update this list on a coincidental basis. Also see Importing (a source code) library when xTIMEcomposer is offline (below). I have used the red ones with success:
- Audio
(Aside: Also see My Beep-BRRR notes and My MEMS microphones notes etc.)
I2S/TDM Librarylib_i2s
Microphone array librarylib_mic_array
S/PDIF librarylib_spdif
Sample Rate Conversion Librarylib_src
(version 1.00 at https://www.xmos.com/file/lib_src-sw/ – download this instead:)- I have on 2May2022 informed XMOS that the newest version is not at the above link, and that it isn’t at all on https://www.xmos.com/libraries/. Version 2.2.0 is at https://github.com/xmos/lib_src where
lib_src-develop
containslib_src
. - Observe that in
lib_dsp
3.1.0, filedsp_os3.h
anddsp_os3.c
the below functions were deprecated for functions inlib_src
instead:dsp_os3_init
(usesrc_os3_init
instead),dsp_os3_sync
(usesrc_os3_sync
instead),dsp_os3_input
(usesrc_os3_input
instead) anddsp_os3_proc
(usesrc_os3_proc
instead)
- I have on 2May2022 informed XMOS that the newest version is not at the above link, and that it isn’t at all on https://www.xmos.com/libraries/. Version 2.2.0 is at https://github.com/xmos/lib_src where
- Board/Device Support
A-Series Support Librarylib_a_series_support
Microphone array board support library
lib_mic_array_board_support
. See My Beep-BRRR notes
OTP reading librarylib_otpinfo
sliceKIT board support librarylib_slicekit_support
Startkit support librarylib_startkit_suport
U-Series Support Librarylib_u_series_support
- Display
Display controller librarylib_display_controller
LCD Librarylib_lcd
- DSP
xCORE-200 DSP Librarylib_dsp
(some functions deprcated forlib_src
instead (see above)) - External Memory
SDRAM Librarylib_sdram
- Networking
Embedded Webserver Librarylib_webserver
Ethernet MAC library liblib_ethernet
TCP/IP Librarylib_xtcp
Time Sensitive Networking Librarylib_tsn
- Programming Utilities
Debug printing librarylib_logging
GPIO Librarylib_gpio
Lightweight assertions librarylib_xassert
Lock handling Librarylib_locks
xCORE C Librarylib_xcore_c
xCORE trycatch librarylib_trycatch
- Serial Peripherals
I2C Librarylib_i2c
SPI Librarylib_spi
UART librarylib_uart
- USB
USB Librarylib_usb
Floating point library
I think the floating point library (mul, div, plus and minus used), that’s included once the compiler sees a need for it, on a (now obsoleted) startKIT (with silicon on board: XS1-A8-DEV) takes about 2584 bytes. On an eXplorerKIT (with silicon on board: XE216-512-TQ128) it takes 2636 bytes. This makes sense, as none of the processors contain any floating point coprocessor or instruction set. These tests are done with a macro that kicks in the four usages (again, one per arithmetic type) of floats instead of integer handling in the code. The linker file should tell the same story. To have the linker create a link file (or linker file, or map file / mapper file in this world) I have added:
xCC_MAP_FLAGS = -Xmapper --map -Xmapper my_map_file.txt
to my makefile, and this pops up as the added code segments that uses the float arithmetics (for the eXplorerKIT):
00043134 0000006c .text __floatunsisf 00043134 00000000 .text $s.12 000431a0 0000007e .text __extendsfdf2 000431a0 00000000 .text $s.21 00043220 0000028c .text __muldf3 00043220 00000000 .text $s.24 000434ac 0000003a .text __fixunsdfsi 000434ac 00000000 .text $s.8 000434e8 000002be .text __divdf3 000434e8 00000000 .text $s.27 000437a8 000002c6 .text __adddf3 000437a8 00000000 .text $s.24 000449ec 0000002a .text __ashldi3 000449ec 00000000 .text $s.4 00044a18 0000002a .text __lshrdi3 00044a18 00000000 .text $s.4
The sizes above add up to 2440 bytes (not 2636), but here is the main diff list, that reflects the same (I don’t know what the lost 2636-2440=196 bytes are used for):
.text 000400f4 000055ac .text 000400f4 00004b60 (diff: 2636 dec) .cp.rodata.cst4 00045a90 000000bc .cp.rodata.cst4 00045044 00000070 (diff: 64 dec)
I assume that the floating point library would take more code for some other combinations of operands. Like in my code it uses fixed unsigned df (diff, I assume) in __fixunsdfsi, for minus; I just assume there would be other combinations. I don’t know where the source code for this is.
I have queried about this at Where to find library code at the xCore Exchange. Here is the answer from robertxmos:
These are found in library files e.g. lib/xs2a/libclang_rt.builtins-xcore.a
As the name suggests, they are the low level libraries that the compiler requires as a minimum runtime environment.
The source code can be viewed at https://compiler-rt.llvm.org/ (we ship with a port of 3.6.0)
How to make a non-binary (source code) library?
See How to make a non-binary (source code) library? at the xCore Exchange. However, here’s the answer from robertxmos:
Yes you can (we do this to ship the standard libraries).
But we don’t support this from our tool chain.The tools for you to do this are available in the tool chain (.o files will be in the .build directory, `xmosar` is the archive tool to package them up).
I would ask you to seek the information else where:
- search for unix-ar e.g. https://www.systutorials.com/docs/linux/man/1-ar/
- `xcc –help` tells you that «-L <directory> Add directory to library search path»
- add `-v` to your makefile xCC_FLAGS to see how other libraries are pulled in, and then add suitable flags using xCC_MAP_FLAGS.
My detailed work-log
11June2018: I managed to compile my library from the application!
Summary
Details in next chapter
- Make your library with «Create new build module based project»
- Make /src and /api directories and think about your file structure
- Add a module_build_info file (if it’s not already there) and fill it with VERSION and DEPENDENT_MODULES. This file is rather nice to use for the in-code version list, something like this:
VERSION=0.8.1 # 0.8.1 24Sep2020 Some text also for git commit # 0.8.0 22Sep2020 Some text --"-- # 0.7.9 11Jul2020 Some text --"--
- The file module_description is set up by the system. It’s supposed to contain aone-liner about the library
- In your application (the one that’s going to use your library) add your library to USED_MODULES
- Move the code proper into your new library
- That’s all! It’s now compiled with your application. If not: restart xTIMEcomposer. The library sources are open
- Now make a git/github version system like this (in Terminal, on the directory). Follow Using git commands to make a local git repository with this command set instead:
git init git add module_build_info module_description api/*.* src/*.* git commit -m Initial
- To export it make a zip of it (exclude the .git directory)
- To import this then unzip to a no-workspace place and get it into your workspave by | File | New | xTIMEcomposer Project Based on Existing Application or Module
The way to it
- First question: I assume I don’t need ar or xmosar before my code is going to be exported to other users? I’m going to find that out! Update: I also now tend to think that the archive tool is used, in this case, more just to get a zip’ing functionality!(?)
- Terminal: open an instance
- Finder: open >/Applications/XMOS_xTIMEcomposer_Community_14.3.3/ (or any other version)
- Drag the SetEnv.command into the Terminal window and hit enter. Now the tools are seen
- I don’t use ar (using zip instead): ar –help or ar –man give the same output:
bash-3.2$ ar --man usage: ar -d [-TLsv] archive file ... ar -m [-TLsv] archive file ... ar -m [-abiTLsv] position archive file ... ar -p [-TLsv] archive [file ...] ar -q [-cTLsv] archive file ... ar -r [-cuTLsv] archive file ... ar -r [-abciuTLsv] position archive file ... ar -t [-TLsv] archive [file ...] ar -x [-ouTLsv] archive [file ...]
- I don’t use xmosar (using zip instead): xmosar –help
bash-3.2$ xmosar --help Usage: xmosar [emulation options] [-]{dmpqrstx}[abcDfilMNoPsSTuvV] [member-name] [count] archive-file file... xmosar -M [<mri-script] commands: d - delete file(s) from the archive m[ab] - move file(s) in the archive p - print file(s) found in the archive q[f] - quick append file(s) to the archive r[ab][f][u] - replace existing or insert new file(s) into the archive s - act as ranlib t - display contents of archive x[o] - extract file(s) from the archive command specific modifiers: [a] - put file(s) after [member-name] [b] - put file(s) before [member-name] (same as [i]) [D] - use zero for timestamps and uids/gids [U] - use actual timestamps and uids/gids (default) [N] - use instance [count] of name [f] - truncate inserted file names [P] - use full path names when matching [o] - preserve original dates [u] - only replace files that are newer than current archive contents generic modifiers: [c] - do not warn if the library had to be created [s] - create an archive index (cf. ranlib) [S] - do not build a symbol table [T] - make a thin archive [v] - be verbose [V] - display the version number @ - read options from --target=BFDNAME - specify the target object format as BFDNAME emulation options: No emulation specific options xmosar: supported targets: elf32-i386 a.out-i386-linux pei-i386 elf64-x86-64 elf32-x86-64 pei-x86-64 elf64-l1om elf64-k1om elf64-little elf64-big elf32-little elf32-big srec symbolsrec verilog tekhex binary ihex Report bugs to <http://www.sourceware.org/bugzilla/>
- xcc –help tells you that
I don’t use -L. Not needed
-L <directory> Add directory to library search path --
-L /Users/teig/workspace/lib_rfm69_xc
My usage, see (ref. below) "library" = _rfm69_xc
It looks like the tradition is to name the directory the same as the built archive (.a) file. This is then found while linking the user’s code with this parameter:
- From xTIMEcomposer user guide [12]:
I don’t use -l. Not needed
10.6 Linker And Mapper Options
The following options control the linker/mapper. -llibrary Searches the library library (sic) when linking. The linker searches and processes libraries and object files in the order specified. The actual library name searched for is liblibrary.a The directories searched include any specified with -L Libraries are archive files whose members are object files. The linker scans the archive for its members which define symbols that have so far been referenced but not defined -- -- -l _rfm69_xc My usage would then expect lib_rfm69_xc.a to have been built
I don’t understand this for the case with libraries with source code that needs to be «compiled in» at compile time, not linked in with relocatable ready-compiled object code. So I think it’s not needed here either. It’s definitively not used in my reference case when I used spi_lib. Just USED_MODULES lib_spi is used then. So xCC_MAP_FLAGS -l is a has nothing to do with this I think.
Now, to make the library:
- File | New | xTIMEcomposer Project
- Project Name: «lib_rfm69_xc»
Create New project in workspace
Create new build module based project
Finish
- Project Name: «lib_rfm69_xc»
- The contents of the module_build_info is jus uncommenting some original contents:
VERSION=0.8.0 DEPENDENT_MODULES = lib_spi lib_xassert MODULE_xCC_FLAGS = $(xCC_FLAGS)
- Now go back to your new application’s makefile. The one that’s going to use the new library. Make sure that the just made library is included in the
USED_MODULES = lib_rfm69_xc lib_spi lib_xassert
list. What happens now is that at before all the probable error essages the compiler will say that:
Using build modules: lib_rfm69_xc(0.8.0) lib_spi(3.0.2-branch-startKIT) lib_xassert(3.0.0)
- Now I need more time to find the optimal file structure and dependencies within my library, since I already have made an application with everything mingled (that code I have published at My aquarium’s data radioed through the shelf chapter «RFM69 library»)
- New | Folder: «api»
- New | Header File: «rfm69_xc.h»
- Analysing XMOS libraries I see that this name is not required. Like lib_startkit_support has startkit_adc.h, startkit_gpio.h and startkit_slider.h. I guess the linker will take what it finds, so I guess I am free on naming
- More analysing of XMOS usage: In most of the libraries that XMOS supply there is only this single file, named like the library without the leading lib_. However, in lib_dsp there are several files in addition to the dsp.h. Their names all start with dsp_xxxx.h. Most of these are #include’d in dsp.h, but not all, like dsp_dct.h which is #include’d in two local files only. However, when I included lib_dsp in USED_MODULES and #include’d <dsp.h> (or <lib_dsp.h> seemed to be accepted as well) then there was a «fatal error: ‘dsp_qformat.h’ file not found».
- All this suits me well, even if nested #include’s is not my favourite. I have a CRC file and a layer of functions in spi_driver.xc, the latter uses lib_spi. These are used in the rfm69_xc only, at the moment – but it’s ok to have them available for others. Not black box on those
- I have now made my library structure. Made new files with correct names and copied over the contents from my old application, since I wanted to have a reference application with the «beta library» code inside the project. Now I can go back there for reassurance
- However, I had lots of problems when building the new application (the one that’s using the new library) with the compiler not seeing the types etc. defined in the new library. I saw that in my new application, in xTIMEcomposer’s «Includes» library my new library was present in addition to other entries:
V Includes lib_rfm69_xc lib_rfm69_xc/api lib_rfm69_xc/src
so there was something right!
- I then stopped xTIMEcomposer (and another instance of it that was also running, see above how this is done) and restarted the first one. I then compiled and it all worked! (I had fiddled with this for hours and been far out, well, you see how long this chapter is..!) Here’s the Build Console’s log (without -v in xCC_FLAGS). The red log is exactly what my reference application had! In other words, my new library is compiled and linked in just like the source files were local. That’s what i wanted.
17:40:10 **** Incremental Build of configuration Default for project _app_rfm69_on_xmos_native **** xmake CONFIG=Default all Checking build modules Using build modules:
lib_rfm69_xc(0.8.0)
lib_spi(3.0.2-branch-startKIT) lib_xassert(3.0.0)
Analyzing rfm69_xc.xc
Analyzing spi_async.xc Analyzing spi_slave.xc Analyzing spi_sync.xc Analyzing xassert.xc Creating dependencies for xassert.xc Creating dependencies for spi_sync.xc Creating dependencies for spi_slave.xc Creating dependencies for spi_async.xc
Creating dependencies for rfm69_xc.xc Creating dependencies for rfm69_spi_driver.xc Creating dependencies for rfm69_crc.xc
Creating dependencies for _app_rfm69_on_xmos_native.xc
Compiling _app_rfm69_on_xmos_native.xc Compiling rfm69_crc.xc Compiling rfm69_spi_driver.xc Compiling rfm69_xc.xc
Compiling spi_async.xc Compiling spi_slave.xc Compiling spi_sync.xc Compiling xassert.xc
Creating _app_rfm69_on_xmos_natives.xe Constraint check for tile[0]: Cores available: 8, used: 4 . OKAY Timers available: 10, used: 4 . OKAY Chanends available: 32, used: 6 . OKAY Memory available: 65536, used: 19204 . OKAY (Stack: 1600, Code: 15974, Data: 1630) Constraints checks PASSED.
Build Complete 17:40:19 Build Finished (took 9s.255ms)
Finishing my work with my first library
- When I now I am going to redistribute this it’s going to be with a plain zip. Not ar or xmosar. But I will exclude the .git directory (below)
- Importing my new application and my new (first!) library into my «workspace2» for my second instance of xTIMEcomposer is like described in My xCORE-200 eXplorerKIT notes (WiFi) [Importing sources from another directory] (Importing sources from another directory)
- When imported and compiled don’t forget to make a Git version control log of your code. Here’s how: My Git/GitHub notes
Importing (a source code) library when xTIMEcomposer is offline
3Apr2021. I have struggled with this! Here is a recipe that I think works. I was into importing lib_mic_array
.
- Did File | New | xTIME composer project |»Create xTIME composer project» and ticked the bottom “Create new build module based project” on. Project name:
lib_mic_array
- This sets up a directory like
/src
, but the whole idea of this is to have thelib_mic_array
directory added - I had downloaded
lib_mic_array
from XMOS servers and placed it at a place not inside workspace - Then «Import from file system» where I had done the download. This imported some structure, but didn’t bring in all the files. Strange
- I let xTIMEcomposer stay open. First, it may be smart to make hidden or invisible files visible in Finder (I’m on a Mac), see 059:[How to see hidden files in macOS]. I then used Finder and opened the
lib_mic_array
position (the one that is somewhere else on the disk, as downloaded from XMOS or Github) with the.cproject
on the top), selected all and copy pasted it into the correct position inside xTIMEcomposer. I told it to overwrite. Everything came in as I wanted.
* Update1: when I importedlib_i2s-5.0.0
I was reminded by trial and error that it wasn’t that directory I should paste into the newly makelib_i2
s, it was the subdirectory which is indeed calledlib_i2s
(because when I tried to build, the xTIMEcomposer soon quit with a lib_i2s is a directory). So,lib_i2s
tolib_i2s
!
* Update2: If you didn’t do as I just described, but both copied and pasted into Finder directories, then xTIMEcomposer won’t detect the files. Then do a Refresh on the imported library in xTIMEcomposer so that the new files will be seen. If xTIMEcomposer doesn’t see it, the files won’t appear to the user either, since it is no Finder.
* Update3: Also, if you had an earlier lib then you may get a warning that some header file is missing (likei2s_tdm_master_impl.h
). After a Clean Project all ok - The first test was to see if it appeared on the libraries list in the Makefile menu. It did!
- And when it built it was included in the list (I had not ticked
lib_dsp
on, it was probably picked up from a dependency inlib_mic_array
):
Using build modules: lib_i2c(4.0.0) lib_logging(2.1.0) lib_mic_array(3.0.1) lib_xassert(3.0.0) lib_dsp(3.1.0)
FreeRTOS
See XMOS FreeRTOS port. This also shows the lib_rtos_support – which is a software library that provides support for running an RTOS on xCORE. Any RTOS!
Erlang vs xC
A very limited compare, but anyway: note 204.
Buffered asynchronous real-time printf
See Debug with printf in real-time [39]: «xTIMEcomposer lets you redirect the standard streams stdout and stderr to an xTAG debug adapter, where the data is buffered until it can be output to the host.» I did test this on one occasion. The prints will appear in xSCOPE. Without this, standard printing (also lib_logging
) will block across cores.
Communities
XCore Exchange
I have referred several XCore Exchange entries directly in the text above. There also are several of these chapters in my other notes where XMOS is contextual in some way. Starting from 22May2023 I will add some here:
- debug_printf and xflash’ed vs. xgdb’ed code (14.4.1) – started by me 22May2023
- xC: different timing on different call sequences (or whatever) – started by me 01Jul2024
References
Wiki-refs: CSP (Communicating Sequential Processes by C.A.R. Hoare), lint, Modula-2, Null pointer, occam, Python, Static analysis, xC.
Observe that all www.xmos.com after 2020 now reroutes to www.xmos.com. Therefore some urls here may be void. But they should be possible to find at the new url. I do try to update rotten links. But then, some time in 2023 these reroute back to https://www.xmos.com again. xcore.ai now are subdirectories like at https://www.xmos.com/ai or https://www.xmos.com/develop/xcore-ai.
- XMOS Programming Guide (as of 17Jan2018: 2015/9/21 version F, 2015/9/18 in the document), see https://www.xmos.com/file/xmos-programming-guide
- xC Reference Manual, 2008/07/16, by Douglas WATT, Richard OSBORNE and David MAY (VERSION 8.7), see https://www.xmos.com/download/private/xC-Reference-Manual%288.7-%5BY-M%5D%29.pdf. Old, can’t even find reserved words «server» or «client» there
- xC Specification, see http://www.xmos.com/xc-specification. 2011, can’t even find reserved words «server» or «client» there
- XS1 Ports: use and specification, 2008, see https://www.xmos.com/file/xs1-ports-specification/
- Introduction to XS1 ports, see https://www.xmos.com/file/xs1-ports-introduction/
- The XMOS XS1 Architecture by David May. Is [16] an newer version? Copyright © 2009 by XMOS Limited. ISBN: 978-1-907361-01-2 (PBK), ISBN: 978-1-907361-04-3 Published by XMOS Limited, see https://www.xmos.com/download/public/The-XMOS-XS1-Architecture%281.0%29.pdf
- Parallel tasks and communication, XMOS, see https://www.xmos.com/published/xmos-programming-guide?version=B&page=23
- Introduction to Programming XMOS Device, not dated, see https://www.xmos.com/support/tools/programming?component=18344
- xTIMEcomposer source code base, see https://www.xmos.com/support/tools/source. I downloaded all the repositories and found no hit for a message like «slave modifier without», so I wouldn’t know where to find the error messages. The answer is probably here: «Some of the xTIMEcomposer Development Tools have been developed under Open Source Licence Agreements. The source for these tools is available for download to satisfy appropriate licence terms.»
- Compact instruction set encoding, patent by David May, XMOS (2007), see https://patents.google.com/patent/US7676653B2/en?q=single_issue&q=dual_issue&assignee=xmos
- XMOS glossary of terms (Version 1.0. October 06, 2014), see http://www.xmos.com/published/glossary. (Update 4Apr2021: This does not seem to be available any more. Mail me to get a copy, since it seems it’s not even in the Internet Archive‘s Wayback Machine, here)
- xTIMEcomposer user guide, see https://www.xmos.com/published/tools-user-guide – 14.0.x of 2015.10.29 is the newest (jan2021). This book contains everything you need to know! Some overlap, I think, with the older [40]
- See [18]
- Programming xC on XMOS Devices, Douglas Watt (2009), see https://www.xmos.com/download/Programming-XC-on-XMOS-Devices(X9577A).pdf. ISBN: 978-1-907361-00-5 (PBK) ISBN: 978-1-907361-03-6 Published by XMOS Limited. On the first page it points to [1] and [3] (above) as «updated versions of this document». That’s true and not. It’s still a good read in itself! I pasted the front page here just to dream closer to having an actual xC paper book in my hand, since it’s not available anywhere(?) not even for a collector. I’m trying to get hold of one, see here
- Benchmarking I/O response speed of microprocessors by Goncalo Martins, Andrew Stanford-Jason and David Lacey at https://www.xmos.com/download/private/Benchmark-Methods-to-Analyze-Embedded-Processors-and-Systems%28X7638A%29.pdf.
– I guess it is a variant of this document: A case for I/O response benchmarking of microprocessors by Goncalo Martins, Dave Lacey, Allistair Moses, Matthew J. Rutherford, Kimon P. Valavanis at http://ieeexplore.ieee.org/document/6389416/?arnumber=6389416 also at researchgate.net (I have requested a copy there). Also see My XMOS notes [14] - The XMOS architecture and XS1 chips by David May. A newer version of [6]? In IEEE Micro 32(6):28-37 November 2012, at https://ieeexplore.ieee.org/document/6341002/ also at researchgate.net (I have requested a copy there).
– I guess it is a variant of this document: XMOS architecture XS1 chips by David May, presented at Hot Chips 23 Symposium (HCS), 2011 IEEE, see https://ieeexplore.ieee.org/document/7477496/. I have read none of them - (A) occam2 toolset user manual, part 1 (User guide and tools). INMOS Limited (Document number 72 TDS 275 02 (72-TDS-275-02), March 1991). See http://www.transputer.net/prog/72-tds-275-02/otdsug1.pdf.
(B) occam 2.1 Toolset User Guide. SGS-THOMSON MICROELECTRONICS (Document number 72 TDS 366 02 (72-TDS-366-02), August 1995). I have not found this scanned on the net, but I have a paper copy - Describe a target platform by XMOS. (Document Number: X8433B, 2014). See https://www.xmos.com/download/private/Describe-a-target-platform%28X8433A%29.pdf
- Tony Hoare, Wikipedia. About his «billion-dollare mistake» see https://en.wikipedia.org/wiki/Tony_Hoare#Apologies_and_retractions
- How to use memcpy with interface array arguments (XMOS, 2013), see https://www.xmos.com/developer/published/how-use-memcpy-interface-array-arguments
- XMOS Timing Analyzer Manual by XMOS, see https://www.xmos.com/file/xmos-timing-analyzer-manual/
- AN00192: Getting Started with Timing Analysis in xTIMEcomposer Studio, by XMOS, see https://www.xmos.com/file/an00192-getting-started-with-timing-analysis-in-xtimecomposer-studio-sw/
- How to define and use a distributable function, by XMOS, see https://www.xmos.com/file/how-define-and-use-distributable-function/ (1.1.1rc0.a, 2013-11-15)
- Communicating Processors Past, Present and Future by David May, Bristol University and XMOS, April 2008, at NOCS, Newcastle. See http://people.cs.bris.ac.uk/~dave/nocs.pdf. (From David May’s home page)
- Multicore Architecture by David May, Oct. 2008. See http://people.cs.bris.ac.uk/~dave/iet2009.pdf. (From David May’s home page)
- XMOS Architecture xC Language by David May, April 2010. See http://people.cs.bris.ac.uk/~dave/xarch2010.pdf. (From David May’s home page)
- XMOS Architecture XS1 Chips by David May, XMOS, not dated, but I guess 2011. See http://people.cs.bris.ac.uk/~dave/hotslides.pdf. (From David May’s home page)
- Clouds, Things and Robots: … the transputer revisited by David May, at International Conference on Parallel Processing (ICPP-2017) (Bristol). See http://www.icpp-conf.org/2017/files/keynote-david-may.pdf
- xCORE-200: The XMOS XS2 Architecture (rev.1.0) by XMOS, 2015/04/01. 289 pages. See https://www.xmos.com/download/xCORE-200:-The-XMOS-XS2-Architecture-(ISA)(1.1).pdf
- XEF232-1024-FB374 Datasheet by XMOS 2016/04. Document Number: X009587. This is a four tile (4 tile) xCORE-200. See https://www.xmos.com/download/XEF232-1024-FB374-Datasheet(1.16).pdf
- XS1-U16A-128-FB217 Datasheet, here: https://www.xmos.com/file/xs1-u16a-128-fb217-datasheet. This is the processor that’s in the startKIT
- xCORE: Multicore theory, hardware and programming by XMOS (2014) – 62 very interesting slides. XMOS had 84 chip variants at the time. xSOFTip looks like the term used for all the different connections the processors could use, and then a full set of libraries. xTIMEcomposer is up and running. The presentation introduces the startKIT, now long gone (but I have had two of them up and running for years). I added some text to slide 36 and used it above: How does xC compare with C? See https://www.xmos.com/download/xCORE:-Multicore-theory,-hardware-and-programming(1).pdf.
– I also (on 27Mar2021) added a reference to the XMOS document https://www.xmos.com/file/xcore-multicore-theory-hardware-and-programming/ page in the Wikipedia article on LLVM, see https://en.wikipedia.org/wiki/LLVM (search for «xC») - How to use transactions over channels by XMOS. Publication Date: 2013/11/15. Shows usage of
slave
andmaster
by letting several communications on the same channel become synchronised rather than one after the other. See https://www.xmos.com/file/how-use-transactions-over-channels - Microphone array XP-USB-MIC-UF216 and processor XUF216-512-TQ128 references at My Beep-BRRR notes
- XFLASH Command-Line Manual by XMOS. See https://www.xmos.com/file/xflash-command-line-manual/
- XS1 Link Performance and Design Guidelines by XMOS, 2013/12/4. Document Number: X2999C. See https://www.xmos.com/file/xs1-l-link-performancedesign-guidelines/ ([36] mentioned in 218). Also see [37]:
- xCONNECT Architecture by XMOS.2013/11/14 Rev A. See https://www.xmos.com/file/xconnect-architecture (referered in [36], above)
- Tools developer’s guide by XMOS. Document Number: XM000140A. 2015/4/21. This was referenced from the XTC Tools 15.0.6 local install documentation as «xCORE 32-Bit Application Binary Interface Information on the XS1 32-ABI, the XE file format and System Call Interface is available in the Tools Development Guide.» See https://www.xmos.com/download/Tools-Development-Guide(2.1).pdf
- Debug with printf in real-time, by XMOS (2013/11/12). Document Number: X1093B, see https://www.xmos.com/download/Debug-with-printf-in-real-time%28X1093A%29.pdf
- Trace data with XScope X9923A with heading Use xTIMEcomposer and xSCOPE to trace data in real-time by XMOS 2013/11/12 doc X9923A Rev C, see https://www.xmos.com/download/Trace-data-with-XScope(X9923A).pdf. I guess you’ll find all this in [12] as well
- I/O timings for xCORE200, by XMOS 2017/2/28. Document Number: XM010258A, see, https://www.xmos.com/download/I-O-timings-for-xCORE200%281.0%29.pdf
- xCORE-200 Clock Frequency Control, by XMOS 2016/10/3. Document Number:
XM010761, see https://www.xmos.com/download/xCORE-200-Clock-Frequency-Control%281.0%29.pdf. Dscussed in lib_i2s I2S/TDM clocked from _internal_ source?