Skip to content

[HAL] Add support for 64 bit us timestamp #4094

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 19 commits into from
Jun 2, 2017
Merged

Conversation

pan-
Copy link
Member

@pan- pan- commented Mar 31, 2017

Description

This patch add support of 64 bit us timestamp in the HAL ticker API.
As a result it is possible to schedule events in more than 2147 seconds as it was previously the case.
The class Ticker, Timer and Timeout takes advantage of this new possibility.

Status

READY

Migrations

If this PR changes any APIs or behaviors, give a short description of what API users should do when this PR is merged.

NO

@pan- pan- changed the title Add support for 64 bit us timestamp [HAL] Add support for 64 bit us timestamp Mar 31, 2017
@pan-
Copy link
Member Author

pan- commented Mar 31, 2017

@0xc0170 @geky @c1728p9 Could you review this PR.
It includes tests describing in detail the behavior expected by the ticker.

@pan- pan- requested review from geky, c1728p9 and 0xc0170 March 31, 2017 14:44
@@ -46,32 +46,39 @@ void Timer::stop() {

int Timer::read_us() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think we can get away with updating this return type?

Copy link
Member Author

@pan- pan- Mar 31, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's an excellent question. I don't have the answer, I choose a conservative approach for this PR even if it hurts to see this return type be int.

us_timestamp_t time = _time + slicetime();
core_util_critical_section_exit();
time = time / 1000;
return (float)read_ms() / 1000.0f;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like time does not get used. Also is this able to return us precision? or is that out of the range of 32-bit floats?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@pan- : time not used?

}

int Timer::read_ms() {
return read_us() / 1000;
core_util_critical_section_enter();
us_timestamp_t time = _time + slicetime();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tangent: The expression _time + slicetime() is repeated quite a bit. Should we actually be adding _time in the slicetime function?

@@ -44,6 +44,10 @@ void TimerEvent::insert(timestamp_t timestamp) {
ticker_insert_event(_ticker_data, &event, timestamp, (uint32_t)this);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is TimerEvent::insert used at all now?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is a public interface, so it might be used by user code.

#include <stddef.h>
#include "hal/ticker_api.h"
#include "platform/mbed_critical.h"

void ticker_set_handler(const ticker_data_t *const data, ticker_event_handler handler) {
data->interface->init();
#define MBED_MIN(x,y) (((x)<(y))?(x):(y))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thought: Should this be static inline to avoid side-effects becoming duplicated?

@geky
Copy link
Contributor

geky commented Mar 31, 2017

This is looking great 👍, Left some review comments.

Another thought (perhaps this should be a separate pr), some devices (NCS36510 for example) only provide a 16-bit timer. Do you think this could be extended to support timers with any bit-width?

@pan-
Copy link
Member Author

pan- commented Mar 31, 2017

@geky Yes it would be really easy to support timer with any bit width if the interface provide this information.

To support a 16 bit timer with the current code. The interface just have to keep state of its counter as a 32 bit counter. The interface can trigger interrupt whenever it wants (at every overflow for instance) without tracking the time requested because the ticker code supports spurious interrupts.

@geky
Copy link
Contributor

geky commented Mar 31, 2017

Just tried this pr with a 16-bit timer and it worked perfectly! This is a great update to the api.

Here's what the NCS36510 16b timer code looks like after this pr: geky@492b558

@pan- pan- mentioned this pull request Apr 5, 2017
Copy link
Contributor

@0xc0170 0xc0170 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 for the tests, extensive, have to have another look at them :-)

Just general comments.

*
* It is important to note that the result will **never** be in the past. If the
* value of the low res timetamp is less than the low res part of the reference
* timestamp then an overflow is
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

then this is overflow or how a user should read this (looks like unfinished sentence).

* @param ref: The timestamp of reference.
* @param relative_timestamp: The timestamp to convert.
*/
static us_timestamp_t convert_relative_timestamp(us_timestamp_t ref, timestamp_t relative_timestamp) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: fix the body { (newline) in some functions in this file (at least new functions, we should fix this file later). plus bool is misaligned below?

us_timestamp_t time = _time + slicetime();
core_util_critical_section_exit();
time = time / 1000;
return (float)read_ms() / 1000.0f;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@pan- : time not used?

hal/ticker_api.h Outdated
* @note prefer the use of ticker_insert_event_us which allows registration of
* absolute timestamp.
*
* @param data The ticker's data
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

data = ticker, plus update the description

hal/ticker_api.h Outdated
*
* @param data The ticker's data
* @param obj The event object to be inserted to the queue
* @param timestamp The event's timestamp
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

relative timestamp in this case


/* Go through all the pending TimerEvents */
while (1) {
if (data->queue->head == NULL) {
// There are no more TimerEvents left, so disable matches.
data->interface->disable_interrupt();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

are we disabling interrupts anywhere? there are 3 removals for disable_interrupt() call. Is this function becoming redundant ? How us ticker/lp ticker achieve the same result (no events - nothing happening) - this question is regarding their implementations, if they do something similar (they do not assume upper layer disables interrupts) ?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interrupts cannot be disabled because the ticker interface doesn't expose anything to monitor overflow of the counter.
The workaround is to track overflow at this layer but it requires to have interrupts always on.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The workaround is to track overflow at this layer but it requires to have interrupts always on.

as in - if none, schedule max delta as it is in update_interrupts, and count overflows here in higher layer. I got it now

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will add a block of documentation detailing the algorithm and how we keeps track of overflow.

}

/**
* update the current timestamp value of a ticker.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

when we are supposed to call this ? Whats the requirement?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

User is not supposed to call this function, it is private (static). It can be called whenever the us_timestamp_t held by a ticker_data_t has to be updated.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My question was from the developer view, once I am about to edit this file, I was not clear when this function is supposed to be called.

* interrupt will be set to MBED_TICKER_INTERRUPT_TIMESTAMP_MAX_DELTA us from now.
* Otherwise the interrupt will be set to head->timestamp - queue->timestamp us.
*/
static void update_interrupt(const ticker_data_t *const ticker) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

update_interrupt vs set_interrupt? Might find a better name for this function?

I believe this is the most important function in this changeset. Where is it then handled that MAX_DELTA is still less than 64bit (if there's event lets say 46bit timestamp set). I assume in the irq handler. I yet have not seen this documented, might be worth adding this here? Something to extend if set_interrupt(MAX_DELTA) then the remaining part is handled ... somewhere... somehow...

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

update_interrupt and set_interrupt are really different and doesn't operate on the same object

update_interrupt(const ticker_data_t *const ticker) update the interrupt driving a ticker_data_t object. The update depends on the state of the object and is encapsulated in the function which is not part of the public interface.

ticker_interface_t::set_interrupt(timestamp_t) set a specific, given timestamp in a ticker_interface.

Do you think of a better name ?

Where is it then handled that MAX_DELTA is still less than 64bit (if there's event lets say 46bit timestamp set). I assume in the irq handler. I yet have not seen this documented, might be worth adding this here? Something to extend if set_interrupt(MAX_DELTA) then the remaining part is handled ... somewhere... somehow...

I'm not sure of fully understand what you want. Can you explain a bit more what you want ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think of a better name ?

Set new match interrupt ? It might not be match in some sense... or just leave it, I was just not clear what that function does from the name

I'm not sure of fully understand what you want. Can you explain a bit more what you want ?

I'll get back to this, have to reread the implementation again

// update the current timestamp
update_current_timestamp(ticker);

// filter out timestamp in the past
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what does this condition handle - if timestamp to be set is less than the time "now" (thats how we cal this in the comments above) ? might be worth adding a comment or its just me

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure of the desirable behavior here: the event can either be dropped and forget or added to the queue and the queue scheduled immediately.

@0xc0170 @geky Thoughts ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added to the queue and the queue scheduled immediately

I would vote for this, no events are forgotten

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 for not forgetting events

}

void ticker_insert_event_us(const ticker_data_t *const ticker, ticker_event_t *obj, us_timestamp_t timestamp, uint32_t id) {
/* disable interrupts for the duration of the function */
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

prefer to have a reason why you are disabling interrupts, its clear we enter/exit critical section. same for the comment above (update the current timestamp - function name is the same to a reader). A reader is interested why there's a need for critical section - what we are protecting here and why

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ticker API is thread safe and interrupt safe therefore access to the ticker has to be protected from external modifications.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was just referring there that the comment either can be removed (obvious that theres critical enter/exit) or we specify a reason why its under critical section. same for the below (update the current time stamp - function name says it).

@0xc0170
Copy link
Contributor

0xc0170 commented Apr 7, 2017

One question - with this work, if we ever get HAL tickers to count overflows, wont be much work to add it in (hypothetically speaking) ? now that we have 64bit timestamp

@pan-
Copy link
Member Author

pan- commented Apr 7, 2017

If the ticker interface provides a function returning the number of time the counter has overflowed then it may simplify the code in mbed_ticker_api.c and won't be hard to integrate.

@theotherjimmy
Copy link
Contributor

bump @pan- Status?

@theotherjimmy
Copy link
Contributor

@pan- Could you rebase? and maybe tell us what's up with this PR (status).

pan- added 11 commits May 16, 2017 10:35
- A new 64 timestamp type has been added: us_timestamp_t.
- Changed type of timestamp in ticker_events iinto us_timestamp_t.
- Event queue now have a to store a current, absolute timestamp.
- Add alternative versions of ticker_insert_event and ticker_read which accept
  in input us_timestamp_t.
- Add documentation explaining the limitation and behavior of ticker_read and
  ticker_insert_event.
The Timer implementation has been refactored to take advantage of this
implementation as the base for all functions reading the time elapsed.
* update_interrupt renamed into schedule_interrupt
* update_current_timestamp renamed into update_present_time
* ticker_event_queue_t::timestamp renamed into
* ticker_event_queue_t::present_time
* Fix doxygen comments in ticker_api.h
* Update comments internal comments in mbed_ticker_api.c
@pan- pan- force-pushed the fix_hal_ticker branch from 251627a to ab09a17 Compare May 16, 2017 09:43
@pan-
Copy link
Member Author

pan- commented May 16, 2017

@0xc0170 @geky I think I've addressed your comments, please review my changes. (1, 2, 3 and 4).
@theotherjimmy PR rebased against master.

@sg-
Copy link
Contributor

sg- commented May 26, 2017

Lets throw a nightly on this to pickup and run the newly added tests against alllll the boards.

/morph test-nightly

@mbed-bot
Copy link

Result: FAILURE

Your command has finished executing! Here's what you wrote!

/morph test-nightly

Output

mbed Build Number: 363

Test failed!

@0xc0170
Copy link
Contributor

0xc0170 commented May 30, 2017

@pan- Can you have a look a the failures? NCS fails ticker tests with this changeset?

@0xc0170
Copy link
Contributor

0xc0170 commented May 30, 2017

-+--------+--------+--------------------+
| NCS36510-GCC_ARM | NCS36510      | tests-mbed_hal-lp_ticker | 1ms lp_ticker          | 1
 | 0      | OK     | 1.05               |
| NCS36510-GCC_ARM | NCS36510      | tests-mbed_hal-lp_ticker | 1s lp_ticker           | 1
 | 0      | OK     | 1.04               |
| NCS36510-GCC_ARM | NCS36510      | tests-mbed_hal-lp_ticker | 1s lp_ticker deepsleep | 1
 | 0      | OK     | 1.07               |
| NCS36510-GCC_ARM | NCS36510      | tests-mbed_hal-lp_ticker | 1s lp_ticker sleep     | 1
 | 0      | OK     | 1.04               |
| NCS36510-GCC_ARM | NCS36510      | tests-mbed_hal-lp_ticker | 500us lp_ticker        | 0
 | 1      | FAIL   | 0.15               |
| NCS36510-GCC_ARM | NCS36510      | tests-mbed_hal-lp_ticker | 5s lp_ticker           | 1
 | 0      | OK     | 5.04               |
+------------------+---------------+--------------------------+------------------------+--------+--------+--------+--------------------+
mbedgt: test case results: 1 FAIL / 5 OK
```

All OK on latest master. same results for IAR, thus al 3 toolchains have the same problem. investigating with Vincent 

@sg-
Copy link
Contributor

sg- commented Jun 1, 2017

any updates? @pan- @0xc0170

pan- added 2 commits June 1, 2017 16:31
* Initialization clear interrupt status
* Remove state in management of interrupt
* Handle timestamp in the past
* Handle current seconds, even if out of the relative timestamp.
* Simplify interrupt handling logic.
@pan-
Copy link
Member Author

pan- commented Jun 1, 2017

@sg- @geky @0xc0170 The fault was on the NCS36510 low power ticker driver, the lp ticker logic was flawed, didn't handle many cases (immediate event, conversion timestamp into seconds counter, ...) and didn't prevented invalid write of the peripheral registers.
I've rewritten a good chunk of it. All tests should be green now on NCS36510.

@sg- sg- added needs: CI and removed needs: work labels Jun 1, 2017
@studavekar
Copy link
Contributor

/morph test-nightly

@mbed-bot
Copy link

mbed-bot commented Jun 1, 2017

Result: SUCCESS

Your command has finished executing! Here's what you wrote!

/morph test-nightly

Output

mbed Build Number: 406

All builds and test passed!

@sg- sg- merged commit fa0cd20 into ARMmbed:master Jun 2, 2017
c1728p9 added a commit to c1728p9/mbed-os that referenced this pull request Jun 6, 2017
When initializing the RTC on Kinetis devices, handle the case where
the time overflow interrupt is pending and the case where the time
alarm flag is pending. These flags persist across reset and if not
handled will cause a crash when powering up the low power ticker.

This problem manifested as a lp_ticker test failure on the K22F and
K64F on CI only when running a nightly. This problem has been present
but was made obvious by PR ARMmbed#4094 which configures all tickers to
interrupt at least every MBED_TICKER_INTERRUPT_TIMESTAMP_MAX_DELTA
(~31 minutes). This caused the RTC alarm to fire 31 minutes after the lp_ticker
or lp_timeout test and caused the next run of the lp_ticker test to
crash on boot.
adbridge pushed a commit that referenced this pull request Jun 11, 2017
When initializing the RTC on Kinetis devices, handle the case where
the time overflow interrupt is pending and the case where the time
alarm flag is pending. These flags persist across reset and if not
handled will cause a crash when powering up the low power ticker.

This problem manifested as a lp_ticker test failure on the K22F and
K64F on CI only when running a nightly. This problem has been present
but was made obvious by PR #4094 which configures all tickers to
interrupt at least every MBED_TICKER_INTERRUPT_TIMESTAMP_MAX_DELTA
(~31 minutes). This caused the RTC alarm to fire 31 minutes after the lp_ticker
or lp_timeout test and caused the next run of the lp_ticker test to
crash on boot.
@pan- pan- deleted the fix_hal_ticker branch November 14, 2018 10:50
@@ -44,9 +44,12 @@ class TimerEvent {
// The handler called to service the timer event of the derived class
virtual void handler() = 0;

// insert in to linked list
// insert relative timestamp in to linked list
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess this was meant to be "relative to the 32-bit cycle" rather than "relative current time". But later contributors assumed the later, and it's only addressed by #14005.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.