XC is C plus X

Started 7April2017, updated 20Jan2018

This page is in group Technology 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 an overview at “My XMOS notes”, chapter MY OTHER NOTES WHERE XMOS MATTERS.

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.

XC == xC

XMOS also calls the language xC with lower-case c [8]. 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 XMOS glossary of terms

I recently (Dec2017) discovered this excellent document! 85 definitions over 17 pages. Read this first! See [11].

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)).

alias [[dual_issue]] out timer
buffered extends par timerafter
chan [[guarded]] pinsneq transaction
chanend in port unsafe
[[clears_notification]] isnull restrict when
client interface select @
clock master server :>
[[combinable]] movable slave <:
combine [[notification]] streaming ==>
core on [[single_issue]] ..
[[distributable]] ordered tile ..

Single and double 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

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: a variable declaration prefixed with on must declare an object of type port or clock
    → Placing something on some hw resource can’t just be something like an int (like I did (14.3.0)
  • error: call makes alias in function `My_Client’ 
    → Same channel is used twice in parameter list (14.2.4)
  • error: cannot declare reference to interface
    → An interface is a bundle of methods and cannot be of type REFERENCE_TYPE (14.3.0)
  • error: client interface cannot select on non slave function
    → In order to use a select it has to be in a client task, not server (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 of select when [[combinable]]. Cannot combine selects from such tasks. Observe that code is not allowed in any of the starred positions: `while(1)*{select{..}*}*' (14.2.4)
  • error: components of multi-tile par must have `on’ specifier or call a service
    → One cannot spread around with on tile and core and not using them just like that (14.2.4)
  • error: declaration statement in ‘par’ statement 
    → A block starting with a par must only contain calls to tasks, not declarations (14.2.4)
  • error: input on an output port (14.3.2)
    → A parameter is out port pin but should have been in port pin or the opposite (14.3.2)
  • 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 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: local variable `port_pin_1B’ has type port
    → A port cannot be a local variable. From [1]: “All ports must be declared as global variables, and no two ports may be initialized with the same port identifier. After initialization, a port may not be assigned to. Passing a port to a function is allowed as long as the port does not appear in more than one of a function’s arguments, which would create an illegal alias” (14.3.2)
  • error: main select in combinable function cannot have default case
    → If it did it couldn’t merge the select loops (14.2.4)
  • error: [[notification]] attribute can only be used on slave functions
    → Don’t forget slave here and usage: [[notification]] slave void notify(void); (14.2.4)
  • error: only local variables of type chan or interface are allowed in a multi-tile main
    → Even if it’s probably not really a multi-tile (I only referred to tile[0]) then main with par does not allow any variables such as int etc. (Reported to XMOS. Better with multi-core?) (14.2.4)
  • error: output to an input-designated port
    → A parameter is in port pin but should have been out port pin or the opposite (14.3.2)
  • 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 placed on something. This must done in main. In order to parameterise this down to a task it must be by reference to that instance in main (14.3.0)
  • 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: 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 since if its's not [[combinable]] (14.2.4)
  • error: `return’ within a par
    → A block starting with a par must be a block, enclosed by curly brackets (14.3.0)
  • 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 on notification within combinable function select case
    → Nested select in a task that is tagged as [[combinable]] (14.2.4)
  • 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
    → (But what about this?) (14.2.4)
  • 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)
  • 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 the red (example): on tile[0].core[4]: (14.2.4)
  • 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: trying to call interface function from a server interface
    → My interface was declared used at server side, but it was client side. Since the usage in code also was client side, my declaration was wrong. It should have been client.. in param list, not server (14.3.0)
  • error: `?’ specified on declarator that is not a reference or a resource
    →  It is not allowed to use an optional parameter in an interface call.
    Not: typedef interface my_i {void command (const unsigned command, const unsigned ?value);} my_i; (14.3.2)
  • xmap: Error: Symbol inP_button_center is a duplicate port declaration.
    → Aliasing of two names on the same port is not allowed (14.2.4)
  • xflashError: F03136 No devices attached – Cannot run the enquirer until a device is found
    → No board connected

Warning messages concerning concurrency

  • warning: cannot have ordered main select in combinable function (directive will be ignored) [-Wunusual-code] → [[ordered]] above select as described in the text not legal (14.3.2)
  • 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)
  • ..

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 from TARGET = STARTKIT to TARGET = 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

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.

Off-piste 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
ch ? value // input
Two single-letter tokens (direction), ch left
go/golang ch <- value // output
value <- ch // input
One two-letter token, same way, ch sided (direction)
xC ch <: value; // output
ch :> value; // input
Two two-letter tokens (direction), different ways, ch left

Using a chanend in both directions

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 chanends (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 chanends 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 chanends.

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

See xC code showing use of replicated select in blog note Channels and rendezvous vs. safety-critical systems.

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;
18 }

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

I needed this with xTIMEcomposer 14.3.2 and macOS HighSierra 10.13.1. If you have 10.13.0 then upgrade.

  • Terminal: open an instance
  • Finder: open /Applications/XMOS_xTIMEcomposer_Community_14.3.2/ (or any other version)
  • Drag the SetEnv.command into the Terminal window and hit enter
  • 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. If not you’ll get Error: F03136 (see above)
  • Type xflash in the Terminal window and drag and drop the .xe file you found in the .bin directory. Hi entre. This would run something like
    xflash /Users/teig/workspace/_Aquarium_1_x/bin/_aquarium_1_x.xe
  • There wil be som logging and a finishing message:
    Site 0 has finished successfully.

Side notes

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.

Other

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.

References

Wiki-refs: lint, Modula-2, occam, PythonStatic analysisXC.

  1. XMOS Programming Guide (as of 17Jan2018: 2015/9/21 version F, 2015/9/18 in the document), see https://www.xmos.com/support/xkits?subcategory=Starter%20Kits&product=17441&component=17653&version=latest
  2. XC Reference Manual, 2008/07/16, by Douglas WATT Richard OSBORNE 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
  3. XC Specification, see http://www.xmos.com/xc-specification. 2011, can’t even find reserved words “server” or “client” there
  4. XS1 Ports: use and specification, 2008, see https://www.xmos.com/zh/download/public/XS1-Ports-Specification(X1373A).pdf
  5. Introduction to XS1 ports, see https://www.xmos.com/download/public/Introduction-to-XS1-ports%28X2738B%29.pdf
  6. The XMOS XS1 Architecture by David May. 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
  7. Parallel tasks and communication, XMOS, see https://www.xmos.com/published/xmos-programming-guide?version=B&page=23
  8. Introduction to Programming XMOS Device, not dated, see https://www.xmos.com/support/tools/programming?component=18344
  9. 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.”
  10. 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
  11. XMOS glossary of terms (Version 1.0. October 06, 2014), see http://www.xmos.com/published/glossary
Email this to someoneShare on Facebook

Leave a Reply

Your email address will not be published. Required fields are marked *

*

* Copy This Password *

* Type Or Paste Password Here *

14,551 Spam Comments Blocked so far by Spam Free Wordpress

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>