XC is C plus X

Contents

Started 7April2017, updated 16Jul2018

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.

Fold handling

This blog note uses the Collapse-O-Matic WordPress plugin to handle text folding. In addition to expanding and closing them individually you may here:

Expand All
Collapse All

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). Also, I haven’t included standard C-type words like enum and break etc. ).

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

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

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 apply [[combine]] to multi-tile par
    → This may be relevant, but see Multi-tile par (14.3.3)
  • error: cannot declare reference to interface
    → An interface is a bundle of methods and cannot be of type REFERENCE_TYPE (14.3.0)
  • error: `c_buttons’ used between two combined tasks ::
    → When I removed the explicit on tile[x].core[y] and added a [[combine]] above the par this message appeared. See [[combine]] and [[combinable]] Here’s what’s illegal, with Button_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 with on 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-level select?

    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 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
    → 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: declaration statement in ‘par’ statement 
    → A block starting with a par 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 a par of [[combinable]] functions (14.3.3)
  • 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 sent c_irq_rising <: 1; and tried to receive case c_irq_rising :> void : since I was not going to need the value (it is always 1). A solution seems to be case c_irq_rising :> unsigned rising in which case the compiler does not warn that rising 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, like case t when timerafter(timeout) :> void: or p_eth_data when pinseq(0xD) :> void: or p_in :> void; (14.3.2)
  • error: input on an output port
    → 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 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: invalid side effect in select
    → I got this together with the “error: trying to call slave function from a client interface” (below) and it disappeared with that error message. But it’s nice to know that both error messages exist! (14.3.3)
  • 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: missing case for interface function `get_register_value’ of interface `read_reg_if’
    → A select with an interface has not got all interface functions defined by the cases supplied, so the select will not do all that it has been defined to do in the interface definition. If this were ok I reckon it would have deadlocked. Nice! (14.3.2)
  • 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
    → This may be relevant. However see Multi-tile par (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 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 case in a [[distributable]] function which is not on an interface
    → I was just testing a [[combinable]] function that is allowed to have a timerafter as the only case of a select by telling the compiler it was [[distributable]] instead (14.3.3)
  • error: select on notification within combinable function select case
    → Nested select in a task that is tagged as [[combinable]] (14.2.4)
  • 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) called startkit_adc was placed wrongly in a par. Also see error: service called in non multi-tile function on XCore Exchange. (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
    → (But what about this?) (14.2.4)
  •  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 enclosing par, it’s enough that the enclosing par is in pair with another [[combine]] par. I don’t really understand this, but it’s probably because any [[combinable]] either must be placed with on or in a [[combine]] par. (14.3.3)
  • 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 dimension num_slaves of the array must correspond to the value num_slaves. Observe that num_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 the red (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 a port array of two bit-wide ports with the same port values, like out port p_ss[NUM_SPI_SLAVES] = {XS1_PORT_1A, XS1_PORT_1A}; (14.3.2)
  • error: timer used in more than one case
    → The same timer is used in more than one select case. Two different timers 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)=> (14.3.2)
  • 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: 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! 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 is extern 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: 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 an interface 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 the interface definition, not the odd  [[independent_guard]] for each select 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)
  • xflashError: 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
    → 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]] above select as described in the text not legal (14.3.2)
  • 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: 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 the array[len] param, like this: static const unsigned len. I had used int instead of unsigned. However, chapter 5.1.3 Variable length arrays in [1] shows int usage here. (14.3.3)
  • 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)
  • ..

Info messages concering concurrency

    • 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 the select. 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 constant false?

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
  • ../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 a chan to send from button tasks. The select case where this chan was picked up was guarded by an expression. When I replaced the chan with an interface that sent from the button client task then the compiler had me add [[guarded]] in the select 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 a chan 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 (Expand all folds to go there). (14.3.3)

“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    }                                                            }
                                                               }
                                                           }

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 t;
        int s;
        t :> s;
        while (1) {
            select {
                case t when timerafter(s) :> void: {
                    c_out <: (s/XS1_TIMER_KHZ); // ms
                    s += 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.

Hidden XC

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 replicated par 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 replicated par

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 t;
  int s;
  int first = 1;
  t :> s;
  while (1) {
    select {
      case first => t when timerafter(s) :> 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;
}
Here are the crucial points pasted from the mail from XMOS. Observe that these points are related to my Ticket31198Code with (DO_PLACED == 1), so it’s about problems around the replicated par construct:

  1. Interface index is wrong on a guarded interface function.
    In your code index_of_client will be wrong when it comes out of the i_notify.start_collect_data case statement. This only happens when the guarded attributed is used
  2. 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
  3. Using the par replicator index for core placement is unimplemented.
    So you can’t do
    on tile[0].core[i] inside a par (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]]

This chapter started with the Replicated par and placements [Problem 31198] chapter. It opened my eyes to the fact that [[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.

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

  1. 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 both chanends in there is because when several select are [[combined]] into one low level select then that same select can not send on a chan 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.”
  2. A [[combinable]] decoration on a task is “used” in one of two ways, as shown in the code both above and below
    1. By a [[combine]] par and no explicit placement like on tile[0].core[0]: ...,
    2. By explicit placements like on tile[0].core[0]: ...
  3. Since chanend is a quite limited resource (each tile has 32. Period) then using interface instead of a chan would make sense. When I changed from 1.) usage of a chan with a [[combinable]] task that was placed on a separate core to 2.) an interface of the sender/client to the receiver/server then I saved 1 core and 2 chanend! I have shown these examples in the XCore Exchange entry I mentioned above. And here’s the new interface definition for that untyped chan; :
    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]]

This is based on the code from XMOS (above). I increased indents from two to four spaces and added some curly brackets plus added som more letters to names. However, the important thing is that I added the 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 t;
    int s;
    int first = 1;
    t :> s;
    while (1) {
        select {
            case first => t when timerafter(s) :> 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 channels

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 chanends 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 chanends! Since chanends are a limited resource (32 per core) 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 t;
        int s;
        t :> s;
        while (1) {
            select {
                case t when timerafter(s) :> void: {
                    c_out <: (s/XS1_TIMER_KHZ); // ms
                    s += 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 t;
       int s;
       t :> s;
       while (1) {
           select {
               case t when timerafter(s) :> void: {
                   i_but.but(s/XS1_TIMER_KHZ); // ms
                   s += 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]]

Coming..

The code: [[distribute]] and [[distributable]]

...

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

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.

The code: A nested select can save you a [[guarded]]

Here three button tasks send “button presses” to one handle task. For each but received in handle (in the outer select) it will trigger two background tasks that will return notify (waited for in the nested select) whereby handle will do a remote procedure call (RPC) to read data from background. The number of button presses being handled from each button does not seem to be fair. Over a 12 hour period I had button 0 cnt 14622, 1 cnt 19628 and 2 cnt 12892. I haven’t figured out why, since that wasn’t the point.

A non-nested select version of handle is shown in the next code fold.

#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]]

Here is the non-nested select version of handle (see previous code fold). I have used [[independent_guard]] on the select cases instead of adding [[guarded]] in the interfaces. The price for that is that the task cannot be [[combinable]] in this case.

I thought that this would have used more chanends, but the nested select and non-nested select solution here (at least) were equal! Both used 6 cores, 6 timers and 11 chanends. 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 interfaces 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;
        }
    }
}

Sharing memory

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 channels 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:

  1. Use unsafe pointers
  2. 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..”
  3. Use inline assembly with GET_SHARED_GLOBAL and SET_SHARED_GLOBAL
  4. Use external C
  5. 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.”

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)

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

Observe that XC also uses => (after a guard in a select case) and -> to tell about return params from interface functions

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. (Update 5Apr2018: And I still need it for High Sierra 10.13.4. No new xTIMEcomposer seen since Oct2017.) More about this here. But here is how to do what the IDE is not able to do by hand:

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

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

  • 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

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

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)

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.

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

  1. Make your library with “Create new build module based project”
  2. Make /src and /api directories and think about your file structure
  3. Add a module_build_info file and fill it with VERSION and DEPENDENT_MODULES
  4. In your application (the one that’s going to use your library) add your library to USED_MODULES
  5. Move the code proper into your new library
  6. That’s all! It’s now compiled with your application. If not: restart xTIMEcomposer. The library sources are open
  7. Make a git system
  8. To export it make a zip of it (exclude the .git directory)
  9. 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
  • 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 CMOS 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

References

Wiki-refs: CSP (Communicating Sequential Processes by C.A.R. Hoare), 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/published/xmos-programming-guide
  2. 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
  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. 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
  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
  12. xTIMEcomposer user guide, see https://www.xmos.com/published/tools-user-guide
  13. Describe a target platform, see https://www.xmos.com/support/tools/documentation?subcategory=Platform%20Configuration%20and%20XN&component=14815&version=X8433A
  14. Programming XC on XMOS Devices, Douglas Watt (2009), see http://www.xmos.com/download/private/Programming-XC-on-XMOS-Devices(X9577A).pdf. 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!
  15. 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). I have not read it
  16. 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
  17. (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
  18. Describe a target platform by XMOS. (Document Number: X8433B, 2014). See https://www.xmos.com/download/private/Describe-a-target-platform%28X8433A%29.pdf

Leave a Reply

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

*

* Copy This Password *

* Type Or Paste Password Here *

18,160 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>