Porting the Arduino millis(), possible? Topic is solved

Technical questions regarding the XTC tools and programming with XMOS.
User avatar
akp
XCore Expert
Posts: 578
Joined: Thu Nov 26, 2015 11:47 pm

Post by akp »

Yes, there is a race condition. But I guess since the variable "g_last_tick" is only used to check for overflow of the timer -- to extend the resolution -- it's going to be in the resolution extension where any errors will be observed, so really it's every 43 seconds you could get some errors I suppose, assuming you call the function with some regularity. Honestly the shared globals don't really save anything, just a few bytes of RAM, so I don't know if it's worthwhile to take the risk. It's better for each core to have its own resolution extension. Each core that calls the function will use its own hardware timer (you can see that if you compile with -report) so I don't think there's any real benefit to the shared globals.

As for the long long, I didn't even know XC wasn't supposed to support 64 bit types. Otherwise I wouldn't have tried it. I am using xTIMEcomposer 14.3.2.


View Solution
User avatar
aclassifier
Respected Member
Posts: 483
Joined: Wed Apr 25, 2012 8:52 pm
Contact:

Post by aclassifier »

akp wrote:Each core that calls the function will use its own hardware timer (you can see that if you compile with -report) so I don't think there's any real benefit to the shared globals.
So a startKIT processor with one slice and 8 cores actually has 8 system timers? And the eXplorerKIT with two slices would have 16? These would be read-only, wouldn't they? Why would that be needed?
akp wrote:As for the long long, I didn't even know XC wasn't supposed to support 64 bit types. Otherwise I wouldn't have tried it. I am using xTIMEcomposer 14.3.2.
Off Topic
Yes, I do use the last 14.3.2 but not on macOS High Sierra. I run it on El Capitan to have it working fluently. See http://www.teigfam.net/oyvind/home/tech ... igh_sierra. I am really awaiting a new xTIMEcomposer/Java pair. I do run it also on High Sierra on another machine, but then I need to Flash from Terminal. Please tell me it's not true any more.
--
Øyvind Teig
Trondheim (Norway)
https://www.teigfam.net/oyvind/home/
User avatar
akp
XCore Expert
Posts: 578
Joined: Thu Nov 26, 2015 11:47 pm

Post by akp »

Sorry, I run it on Win7 x64 and have run it on Ubuntu but not Mac OSX. I have instantiated 8 test_task() and this is what -report gives me. It seems my code uses 1 timer for something and then 1 per core.

Code: Select all

Creating millis.xe
Constraint check for tile[0]:
  Cores available:            8,   used:          8 .  OKAY
  Timers available:          10,   used:          9 .  OKAY
  Chanends available:        32,   used:          1 .  OKAY
  Memory available:       65536,   used:       5936 .  OKAY
    (Stack: 2384, Code: 2848, Data: 704)
Constraints checks PASSED.
Build Complete
User avatar
aclassifier
Respected Member
Posts: 483
Joined: Wed Apr 25, 2012 8:52 pm
Contact:

Post by aclassifier »

One timer per core matter:
TARGET = STARTKIT

Code: Select all

Constraint check for tile[0]:
  Cores available:            8,   used:          4 .  OKAY  
  Timers available:          10,   used:          4 .  OKAY
I have always thought that these were not the timer counter hw but how many "timer" statements that I could have in XC?

And that that was, in some mysterious way mapped to "Timers available" (without thinking, really). Then there is 10 on the startKIT? Will it take the other 2 from the other slice that runs the debugger?, then?
--
Øyvind Teig
Trondheim (Norway)
https://www.teigfam.net/oyvind/home/
User avatar
aclassifier
Respected Member
Posts: 483
Joined: Wed Apr 25, 2012 8:52 pm
Contact:

Post by aclassifier »

I see in f.ex. [1] that there are
Hardware resources
• 12 clock blocks (6 per tile)
• 20 timers (10 per tile)
• 8 locks (4 per tile)
Perhaps indicating that there is not really one timer per core. But, like 10 per tile, each tile 8 cores.

It could be one of these timers per XC timer, but then with the strong optimalisation that xcc has, it could be less.

Like, with chanends: it's not always two per chan. It depends on how a task runs and how it shares its select with another task. I tried to get an answer on that once, but failed to [2]. [3], however, did get some response. I guess that there is only one system timer per chip or.. per tile?

However I do notice that I am only allowed to have one timer in any select, indicating that you akp, must have some right here! (I haven't checked nested selects). But I have seen no restrictions on timers in [[combinable]] tasks running on the same logical core. Also, there is the setclk instruction [4] with the text "Set clock for a resource". Resource = thread or core?

Then in the [1] again I see in Figure 1 that there are "xTIME: schedulers, timers, clocks" which confuses me further. Probably XMOS hasn't been 100% concise with wording.

I would certainly like someone to tell me how this really is. Further than my speculations above. I am after timer timers, not clock blocks or port timing.

[1] "XS1-U16A-128-FB217 Datasheet" (XM002326)
[2] Calculating number of chanends
[3] Using a chanend in both directions
[4] "Assembly Programming Manual" (X9432B)
--
Øyvind Teig
Trondheim (Norway)
https://www.teigfam.net/oyvind/home/
User avatar
akp
XCore Expert
Posts: 578
Joined: Thu Nov 26, 2015 11:47 pm

Post by akp »

I have to admit I am not an expert myself on the ins and outs of the XMOS hardware, but I suspect you are on to something with your discovery with respect to select. I think if you try to select on 2 timeouts, you will need 2 timers -- presumably the underlying hardware for a timer is really something like a compare register that compares to the tile hardware timer? If you look in the right document you can probably find out. I can say I have some combinable code (bidirectional UART) that runs with 2 timers, one for TX and one for RX bit rate clock. I can instantiate the code combined or not combined and it works OK.
User avatar
aclassifier
Respected Member
Posts: 483
Joined: Wed Apr 25, 2012 8:52 pm
Contact:

Post by aclassifier »

You are right, akp. Select/case on the same timer causes

Code: Select all

error: timer used in more than one case
even if a guard would only allow one of them to be selected. Like (value==1)=> in one and (value==2)=> in the other, which didn't make it any more legal.

When I added another timer in a select it's ok. It must be, thinking it over (and I have been used to it in "all" the CSP-type framework/languages I have seen).
Off Topic
I've even written a paper on different type of timers for a framework we did at work (New ALT for Application Timers and Synchronisation Point Scheduling).
However, I got the same

Code: Select all

Timers available:          10,   used:          4 .  OKAY
when I added the extra timer. Even if I made sure that they did individual things, so that as far as I can see the code couldn't have been optimised away. The task is [[combinable]].

Now, someone at XMOS should set things straight!?

By the way, the error text above would better have been

Code: Select all

error: the same timer used in more than one case
--
Øyvind Teig
Trondheim (Norway)
https://www.teigfam.net/oyvind/home/
User avatar
aclassifier
Respected Member
Posts: 483
Joined: Wed Apr 25, 2012 8:52 pm
Contact:

Post by aclassifier »

By the way, I have posted this: XC and long long, float, double and long double
--
Øyvind Teig
Trondheim (Norway)
https://www.teigfam.net/oyvind/home/
User avatar
aclassifier
Respected Member
Posts: 483
Joined: Wed Apr 25, 2012 8:52 pm
Contact:

Post by aclassifier »

I am testing! With the non-global solution I get "error: use of `millis_state' violates parallel usage rules" on the par in main. You say that you have a race in the global solution (that I absolutely would not want) (but that I could accept if it will never happen if I always use it more often than about 43 seconds (?), because that's the limitation of a standard timer's timerafter as well).

I could make such a system per par component (=task). I of course also get this if the two places where MILLIS() is used is placed on the same core.

I tend to start to think that the solution I have shown that works well here that's based on standard timer usage, or the 0.65536ms resolution code in this thread (and then use more ticks.. like 1526 per second) would be easier. In my real code I have wrapped the standard pattern into a function like this, with error handling and all, and I now think I will stay with it (I am working with the RFM69HCW radio board from adafruit):

Code: Select all

// INTERNAL FUNCTION
uint8_t // readFromRegValue
while_readRegExpectedMask (
        const uint8_t        readFromReg,     // Like REG_IRQFLAGS1
        const uint8_t        readFromRegMask, // Like RF_IRQFLAGS1_MODEREADY
        const bool           invertedLogic,   // like LOGIC_NORMAL
        const unsigned       timeout_ms,      // Like 200
        const unsigned       error_bit,       // like ERROR_BIT_RF_IRQFLAGS1_MODEREADY
        client spi_master_if i_spi,           // For writeReg and readReg macros
        spi_params_t         &spi_params,     // --"--
        rfm69_internals_t    &radio)
{
    timer    tmr;
    time32_t now_time_ticks;
    time32_t start_time_ticks;
    time32_t timeout_at_ticks;
    bool     timed_out;
    uint8_t  readFromRegValue;
    bool     return_read_ok;

    debug_print ("%s","loop?\n");
    tmr :> start_time_ticks;
    timeout_at_ticks = start_time_ticks + (timeout_ms * XS1_TIMER_KHZ);

    do {
        readFromRegValue = readReg (readFromReg); // readReg
        return_read_ok   = ((readFromRegValue bitand readFromRegMask) != 0) xor invertedLogic;

        if (not return_read_ok) {
            tmr :> now_time_ticks;
            timed_out = AFTER_32 (now_time_ticks, timeout_at_ticks);
            if (not timed_out) {
                 // Do another round
            } else {
                radio.error_bits or_eq (1<<error_bit);
                debug_print ("%s","timeout!\n");
            }
        } else {} // Finished

    } while ((not return_read_ok) and (not timed_out));
    return readFromRegValue; // Plus radio.error_bits
}
--
Øyvind Teig
Trondheim (Norway)
https://www.teigfam.net/oyvind/home/
User avatar
akp
XCore Expert
Posts: 578
Joined: Thu Nov 26, 2015 11:47 pm

Post by akp »

What is the minimal code that causes the parallel usage rules error? I seem to be able to use it ok.
Post Reply