-
Notifications
You must be signed in to change notification settings - Fork 6.7k
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
[DNM] [RFC] drivers: counter: API proposal unifying counter and rtc drivers. #8340
[DNM] [RFC] drivers: counter: API proposal unifying counter and rtc drivers. #8340
Conversation
CC: @nvlsianpu @kl-cruz |
cc @cvinayak |
Codecov Report
@@ Coverage Diff @@
## topic-counters #8340 +/- ##
==================================================
- Coverage 53.11% 48.34% -4.77%
==================================================
Files 218 264 +46
Lines 26868 42164 +15296
Branches 5952 10135 +4183
==================================================
+ Hits 14271 20386 +6115
- Misses 10162 17700 +7538
- Partials 2435 4078 +1643
Continue to review full report at Codecov.
|
Can we make some of these APIs available to user mode? |
include/counter.h
Outdated
typedef u32_t (*counter_api_get_pending_int)(struct device *dev); | ||
typedef int (*counter_api_ms_to_ticks)(struct device *dev, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we really need separate us/ms calls?
Could we just use us as a unit?
What about tick to ms/us conversion?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
u32_t limits us. 2^32 microseconds is 4294 seconds. Ticks to ms/us - why not?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
us_to_ticks is enough indeed.
What would be the use case for the reverse function?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
us_to_ticks might not be enough. Consider the case when you have RTC prescaled to have 1hz freq and you want to get number of ticks for 12h timeout. 12h (1236001000000 us) needs 36 bits to represent. Unless we use u64_t for us argument.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is there such use case of counting for so many hours? (if so, then yes u64_t it would be)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see a use case when you would like to put device to very long sleep (e.g. 2 days). Normally, you could use system timer, but it operates on system clock (e.g. 32 khz). 2 days on 32k does not fit in 32 bits. Then you can take counter.h implementation on RTC, set frequency to 1hz and then you can set alarms for much longer periods (more than a month).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In my opinion we should use 64-bit value with us as a unit. This gives us the most flexibility,
include/counter.h
Outdated
* counter initial value is set to zero. If it is a 'countdown' counter, | ||
* the initial value is set to the maximum value supported by the device. | ||
* Start the counter device. The counter initial value is set to zero. Counter | ||
* wraps at 0xffffffff. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What about about 16, 24 and 64-bit counters?
What about counting down counters?
A more general questions:
- Are we going to create low-level API (exposing HW directly) or a higher level thing which implements a "generic" timer/counter?
- Are we going to use such timer/counter as a system tick generator (now we need dedicated driver exposing specific API).
- What about counting external events (not time related). How to configure counter clock source, prescaler and so on.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This API is meant to be generic. When it comes to hw specific stuff (such as clock source, etc...) that's up to the driver.
16, 24 bits counter -> that fits in 32.
Is there 64 bits counter on 32 bits arch? (could be unrelated, thus why I am asking)
counting down/up does not matter much, besides driver implementation that is. I guess.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes, my plan was to hide 16,24 bits capability inside the driver. If hw does not support it then it must emulate it. Otherwise we loose abstraction. And if user is aware of hw limitation set_overflow can set top value within the limiation and emulation will not be used.
64 bits - i don't know. Any generic interface cuts extremes. If 64bit general purpose timer is present and needed it will have to go through custom driver. If zephyr supports more and more devices that have 64 bit timers i would prefer that API is extended with dedicated 64 bit functions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Counting up/down matters if you would like to specify absolute value. If current is tick count is 100 and you specify 200 as a next alarm, you will get different results when your counter counts up and down :) Of course we can assume that our timer model always counts up (then lower level will have to emulate that).
Regarding bits emulation: This will be tricky. We cannot just shift timer value, as we loose precision in such case. Without shift, we will have to support alarms set over several timer overflows and similar corner cases. This will result in number of very similar but complex timer drivers. Moreover such "emulation" already exists in the upper layers of the system (look for kernel timers).
IMHO we will need more than 1 layer here:
- Abstraction of HW (number of bits, up/down, frequency, prescaler/clock source selection, callback on compare and overflow).
- Generic layer translating HW into generic timer (32-bit, always up, multiple single-shoot and periodic notifications)
- Interface allowing selecting one of generic timers as a system clock source.
- Interface allowing using selected generic timer in userspace.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree that 32bit emulation is generic and could be done by layer above the driver to make the driver simpler. Probably same with up/down. Configuration of counting source (clock vs other events) is hw dependent and should be done outside (e.g. kconfig option specific to driver implementation or external call to lower layer driver).
In order to make counter.h closer to hw and allow building generic timer on that it would need following changes:
- renaming counter_set_overflow to something like counter_set_top() which would set overflow/reload value depending on direction.
- adding function counter_get_max_top() which would return capacity of the driver.
- adding counter_get_max_alarm() which would return maximal value that can be user for alarm (duration and period)
- adding counter_get_dir() - up or down. I assume that direction would be fixed and configured at compile time (if hw supports up and down)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I also see now a need to add ticks_to_us(). It is needed to put counter_read() value on scale.
@andrewboie: How do you see handling that in the userspace? Are you thinking about interrupt-like interface (similar to Unix signals) or just about ability to wakeup a thread when event will be emitted? |
include/counter.h
Outdated
bool abs; | ||
u32_t ticks; | ||
counter_single_shot_callback_t callback; | ||
void *user_data; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Couldn't we do like in gpio: user CONTAINER_OF(), the callback would pass the pointer of the struct counter_single_shot then, saves a pointer on function signature and in this struct.
include/counter.h
Outdated
void *user_data); | ||
|
||
struct counter_single_shot { | ||
/** Index of the request. Maximal index is implementation dependent.*/ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you meant device driver dependent? maybe it's better to hide that, and if there are slots: fine, if not then return an error.
include/counter.h
Outdated
/** Index of the request. Maximal index is implementation dependent.*/ | ||
u8_t id; | ||
/** If true ticks are treated as absolute value, else it is relative to | ||
* the counter reading performed during the call. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would assume relative should be the only way. I don't get why there would be 2 different ways.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Absolute value is needed to implement precise periodic timer as you have to schedule new event in relation to last event compare value, not current tick count.
@@ -132,7 +205,7 @@ static inline int counter_set_alarm(struct device *dev, | |||
* | |||
* @param dev Pointer to the device structure for the driver instance. | |||
* | |||
* @retval 1 if the counter interrupt is pending. | |||
* @retval 1 if the counter any interrupt is pending. | |||
* @retval 0 if no counter interrupt is pending. | |||
*/ | |||
__syscall int counter_get_pending_int(struct device *dev); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As we are at reworking the whole API, can we challenge that function? is it really useful?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am fairly new to zephyr. I've seen that it is mainly used to determine wakeup source. Is it only for testing?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not sure why it's there. We have it also for gpio, aio... But it's unused anywhere, I don't see why user code would need to know about pending interrupts, moreover that we are fully interrupt based. Unless you lock interrupt for a long time, and want to behave as a polling-based code.
The only code using it is a PM test. I kind of get why it does that there, but perhaps there is another way.
Let's see next device driver API meeting, will put that on the agenda.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's the conclusion, is this function to stay?
include/counter.h
Outdated
{ | ||
struct counter_driver_api *api; | ||
|
||
api = (struct counter_driver_api *)dev->driver_api; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
assign api at its declaration, like it's done elswhere.
include/counter.h
Outdated
{ | ||
struct counter_driver_api *api; | ||
|
||
api = (struct counter_driver_api *)dev->driver_api; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ditto
include/counter.h
Outdated
typedef u32_t (*counter_api_get_pending_int)(struct device *dev); | ||
typedef int (*counter_api_ms_to_ticks)(struct device *dev, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
us_to_ticks is enough indeed.
What would be the use case for the reverse function?
include/counter.h
Outdated
typedef int (*counter_api_ms_to_ticks)(struct device *dev, | ||
u32_t ms, | ||
u32_t *ticks ); | ||
typedef int (*counter_api_us_to_ticks)(struct device *dev, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would just return the ticks. I don't see any error that could happen (maybe I am wrong)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
returning ticks would simplify conversion. The only thing that could go wrong is when requested ms/us does not fit in 32b ticks. In that case function would saturate at max ticks. Of course, having ticks as return value has 'style' advantage ( counter_set_overflow(... counter_us_to_ticks(1000))).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@tbursztyka: We will need reverse functions for precise time measurement. We can use k_cycle_32_get() for that, but on some architectures this is bound to the most energy efficient timer (read: low frequency). There are faster timers in the system, but there is no official API for controlling them.
Same APIs just write handlers for those APIs which can be exposed to user mode. There's lots of example of this in the other driver subsystems. Looking at this API, you could expose all the APIs which don't take callback functions or struct counter_single_shot as parameters (since we don't allow registering callbacks which run in IRQ context from user mode). Don't need to do it in this PR, can leave for another day. |
I am considering the fact that we may get rid of the ticks/count exposure altogether. Instead user would provide 1 unique parameter: time. Time is something generic that user understands, and thus would not have to manage ticks directly. We have to get decided on µs or ms. I guess there is a use-case for µs so we would have to stick with it, and thus maybe u64_t. Function could return -ERANGE if really it can't handle given time (i.e.: time to ticks below in driver overflows). What do you think? |
Time is valid for timer, which is a counter counting clock cycles. But counter could also count some other things. If we would like to have unified API for all type of counters (including timers), we will have to provide access to raw ticks. |
96c1430
to
511ffbc
Compare
Pushed update:
|
The CMSDK Timer can be used as a timer or as a counter. The unified interface proposed in zephyrproject-rtos#8340 unifies counter.h and rtc.h to provide a common interface. This patch modifies the timer implementation of the single timer to make it compliant with the new proposed interface. Signed-off-by: Vincenzo Frascino <[email protected]> Signed-off-by: Kumar Gala <[email protected]>
The CMSDK Dual Timer can be used as a timer or as a counter. The unified interface proposed in zephyrproject-rtos#8340 unifies counter.h and rtc.h to provide a common interface. This patch modifies the timer implementation of the dual timer to make it compliant with the new proposed interface. Signed-off-by: Vincenzo Frascino <[email protected]> Signed-off-by: Kumar Gala <[email protected]>
The CMSDK Timer can be used as a timer or as a counter. The unified interface proposed in #8340 unifies counter.h and rtc.h to provide a common interface. This patch modifies the timer implementation of the single timer to make it compliant with the new proposed interface. Signed-off-by: Vincenzo Frascino <[email protected]> Signed-off-by: Kumar Gala <[email protected]>
The CMSDK Dual Timer can be used as a timer or as a counter. The unified interface proposed in #8340 unifies counter.h and rtc.h to provide a common interface. This patch modifies the timer implementation of the dual timer to make it compliant with the new proposed interface. Signed-off-by: Vincenzo Frascino <[email protected]> Signed-off-by: Kumar Gala <[email protected]>
The CMSDK Timer can be used as a timer or as a counter. The unified interface proposed in #8340 unifies counter.h and rtc.h to provide a common interface. This patch modifies the timer implementation of the single timer to make it compliant with the new proposed interface. Signed-off-by: Vincenzo Frascino <[email protected]> Signed-off-by: Kumar Gala <[email protected]>
The CMSDK Dual Timer can be used as a timer or as a counter. The unified interface proposed in #8340 unifies counter.h and rtc.h to provide a common interface. This patch modifies the timer implementation of the dual timer to make it compliant with the new proposed interface. Signed-off-by: Vincenzo Frascino <[email protected]> Signed-off-by: Kumar Gala <[email protected]>
The CMSDK Timer can be used as a timer or as a counter. The unified interface proposed in #8340 unifies counter.h and rtc.h to provide a common interface. This patch modifies the timer implementation of the single timer to make it compliant with the new proposed interface. Signed-off-by: Vincenzo Frascino <[email protected]> Signed-off-by: Kumar Gala <[email protected]>
The CMSDK Dual Timer can be used as a timer or as a counter. The unified interface proposed in #8340 unifies counter.h and rtc.h to provide a common interface. This patch modifies the timer implementation of the dual timer to make it compliant with the new proposed interface. Signed-off-by: Vincenzo Frascino <[email protected]> Signed-off-by: Kumar Gala <[email protected]>
Proposal of the API that would unify rtc.h and counter.h. It is based on existing counter.h and is backward compatible (counter_set_alarm renamed to counter_set_overflow, compatibility define added).
See related issue: #8331
API proposal extends counter.h API by:
Signed-off-by: Krzysztof Chruściński [email protected]
This change is