Counter Init() parameters, cycles() and IRQ()#163
Conversation
f4cdb6a to
561999a
Compare
561999a to
b3bfbff
Compare
af7dd77 to
5450eba
Compare
IFX-Anusha
left a comment
There was a problem hiding this comment.
A lot of work here 🔥 — the overall structure is good. I have tried my best to review the code (with some AI assistance (of course) ) and found a few things.
5450eba to
4b5830b
Compare
|
|
||
| .. note:: | ||
|
|
||
| - IDs ``0`` to ``31`` are available (same TCPWM mapping as ``Timer``: 32-bit IDs ``0-7``, 16-bit IDs ``8-31``). |
There was a problem hiding this comment.
Can we use any tcpwm instance? or it has to be the one associated to that the used pin?
There was a problem hiding this comment.
We can use any tcpwm instance from 0-31 as mentioned.
The pin associated to the Counter is only for the input trigger (denoted by the src arg in Init()). This is pin P11_1 in our PSOC-Edge board. It is not a TCPWM capable pin.
There was a problem hiding this comment.
Do we actually want to expose that TCPWM information to the user?
I guess it would be only valuable if he has to decide 16 or 32 resolution. But that can be also be resolved based on the max/min.
I would say this id should match more the unit peri input channel. So that you could actually avoid passing the Pin value.
And this is always the problem with the id... deciding what is represents.
In the SCB classes it matches the SCBx index. But here we even have PERI0 a PERI1 ... So actually I am not fully sure.
There was a problem hiding this comment.
The ID information is shared with the user to make them aware of the 16-bit or 32-bit counter.
The pin input is a requirement from the API as the trigger input only, which in our port's case is only P11_1.
ID has different meanings in different classes. So its better to document it and let the user know and decide what to use.
There was a problem hiding this comment.
That is exactly my question, the 'id' meaning for this class:
a) 'id' = counter input channel
b) 'id' = tcpwm associated counter
I don't see the b.
That can be resolved based on the min, max values. And 'id' = channel = hardware pin.
So you can do:
c = Counter(1)
c = Counter(Pin.cpu.P11_1)
c = Counter(1, Pin.cpu.P11_1)
and these are all the same.
Assuming that P11_1 is in channel 1.
There was a problem hiding this comment.
In the Counter class :
a) 'id' = tcpwm associated counter
b) 'src' = counter input channel
Both parameters are very different. We can have one src (P11_1) and multiple ids.
So this becomes a necessity that we support both :
c = Counter(1, Pin.cpu.P11_1)
OR
we keep src as a default P11_1 and this works :
c = Counter(1)
There was a problem hiding this comment.
I guess I am not expressing myself very well...
The alternate function PERIx_TR_IO_INPUTx has up to 8 possible inputs (even if for this board only one is available).
I would say this should be the id instead of the tcpwm counter.
Given how other ports use this id, and how we have implemented that in the UART.
The user does not need to know to which tcpwm counter we are associating the counter.
That can be automatically derived from the the min, max value, based on required resolution.
Then, the user can create a counter either based on the pin, or the PERIx_TR_IO_INPUTx.
He could pass both also, then they need to match.
|
|
||
| - IDs ``0`` to ``31`` are available (same TCPWM mapping as ``Timer``: 32-bit IDs ``0-7``, 16-bit IDs ``8-31``). | ||
| - Constructing ``Counter(id)`` again while that instance is active raises ValueError instead of returning/reinitialising the existing instance. Call ``deinit()`` first. | ||
| - ``src`` must be a pin with ``PERI_TR_IO_INPUT`` routing. On KIT_PSE84_AI these are: ``P11_1``, ``P11_3``, ``P7_7``, ``P8_0``. |
There was a problem hiding this comment.
I know right now we have only one baord, but we should not add board specific information in this section to keep it generic.
We should show which pins are counter capable in the pinout diagram. Check with @ederjc about how to add the counter info.
| return; | ||
| } | ||
|
|
||
| cy_rslt_t rslt = mtb_hal_clock_set_peri_clock_freq(&CYBSP_GENERAL_PURPOSE_TIMER_clock_ref, |
There was a problem hiding this comment.
Please do not use the MTB HAL. This is uncertain to be supported in the future.
The port implementation should be based on PDL only.
Once the clk.h class is merged (soon), we should handle the clock from there.
b47730c to
5e50167
Compare
Signed-off-by: NaveenChengappa-IFX <Naveen.Chandura@infineon.com>
Signed-off-by: NaveenChengappa-IFX <Naveen.Chandura@infineon.com>
Signed-off-by: NaveenChengappa-IFX <Naveen.Chandura@infineon.com>
Signed-off-by: NaveenChengappa-IFX <Naveen.Chandura@infineon.com>
Signed-off-by: NaveenChengappa-IFX <Naveen.Chandura@infineon.com>
Signed-off-by: NaveenChengappa-IFX <Naveen.Chandura@infineon.com>
Signed-off-by: NaveenChengappa-IFX <Naveen.Chandura@infineon.com>
Signed-off-by: NaveenChengappa-IFX <Naveen.Chandura@infineon.com>
Signed-off-by: NaveenChengappa-IFX <Naveen.Chandura@infineon.com>
Signed-off-by: NaveenChengappa-IFX <Naveen.Chandura@infineon.com>
Signed-off-by: NaveenChengappa-IFX <Naveen.Chandura@infineon.com>
Signed-off-by: NaveenChengappa-IFX <Naveen.Chandura@infineon.com>
Signed-off-by: NaveenChengappa-IFX <Naveen.Chandura@infineon.com>
Signed-off-by: NaveenChengappa-IFX <Naveen.Chandura@infineon.com>
Signed-off-by: NaveenChengappa-IFX <Naveen.Chandura@infineon.com>
Signed-off-by: NaveenChengappa-IFX <Naveen.Chandura@infineon.com>
Signed-off-by: NaveenChengappa-IFX <Naveen.Chandura@infineon.com>
Signed-off-by: NaveenChengappa-IFX <Naveen.Chandura@infineon.com>
Signed-off-by: NaveenChengappa-IFX <Naveen.Chandura@infineon.com>
Signed-off-by: NaveenChengappa-IFX <Naveen.Chandura@infineon.com>
3206c50 to
252bdf0
Compare
| @@ -391,17 +891,34 @@ | |||
| mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("not initialised")); | |||
There was a problem hiding this comment.
Is there a situation in which we could use a counter object which has not been initialized?
I would say once we do obj.value([]) that should not the case.
There was a problem hiding this comment.
The user can do something like this :
Start off with a bare minimum constructor like c = Counter(0) , and then without initializing it with c.init() , if there is a request for c.value() then this error is thrown.
It is acting like an information to the User stating they have forgotten to c.init() before requesting for the value.
And I dont see any usecase for a bare constructor without init().
There was a problem hiding this comment.
As far I understand, for example for UART, if you do UART(0), this would already enable the UART for 115200, 8-N-1 and return a usable object.
I would say that c = Counter(0) should be initialized to certain values with default and/or mandatory args?
Is there any other machine.xxx() with this behavior?
There was a problem hiding this comment.
It will work. The only missing mandatory parameter (without a default value) is the src. This is what throws the exception of not initialised when we ask for Counter.value().
So if we can have pin P11_1 set as default src , then this error message will not come up.
We can still do it in our case because we have only P11_1 that can be set as src .
There was a problem hiding this comment.
We have to think further than this board, we enabling the MCU.
So potentially with some future board could also use these pins. P7_0, P7_1, P7_5 - P8_0, P11_0 - P11_3.
We should not set any as default.
The src parameter is set already as MP_ARG_REQUIRED -> https://github.com/Infineon/micropython-psoc-edge/blob/Counter_cyclic_and_irq/ports/psoc-edge/machine_counter.c#L518
But machine_counter_init_helper() is only called if there is more than 1 positional argument or more than 0 keyword arguments:
https://github.com/Infineon/micropython-psoc-edge/blob/Counter_cyclic_and_irq/ports/psoc-edge/machine_counter.c#L810-L814
if (n_args > 1 || n_kw > 0) {
mp_map_t kw_map;
mp_map_init_fixed_table(&kw_map, n_kw, args + n_args);
machine_counter_init_helper(self, n_args - 1, args + 1, &kw_map);
}
If that condition is removed. Just:
mp_map_t kw_map;
mp_map_init_fixed_table(&kw_map, n_kw, args + n_args);
machine_counter_init_helper(self, n_args - 1, args + 1, &kw_map);
Then if no src is passed, it will throw exception:
>>> from machine import Counter
>>> c = Counter(0)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'src' argument required
>>> from machine import Pin
>>> c = Counter(0, Pin.cpu.P11_1)
Then that initialization check in value() would not be required.
Unless that behavior of uninitialized instance Counter(id) object is somehow required. I would say that calling the constructor should also initialize the object.
| machine_counter_restore_src_pin(self); | ||
|
|
||
| // Ensure shared timer clock is configured and bound to this counter PCLK. | ||
| machine_counter_configure_clock(); |
There was a problem hiding this comment.
To be refactored based on shared clock. Maybe via tcpwm class when we refactor it.
Signed-off-by: NaveenChengappa-IFX <Naveen.Chandura@infineon.com>
Signed-off-by: NaveenChengappa-IFX <Naveen.Chandura@infineon.com>
| if (n_args == 2) { | ||
| // Freeze counting during read/reset so no new edges arrive in this window. | ||
| Cy_TCPWM_Counter_Disable(TCPWM0, self->counter_num); | ||
| } |
There was a problem hiding this comment.
Is this required? I expect you should be able to read the counter while active without any race conditions or such.
We can miss some edges here or?
There was a problem hiding this comment.
Okay, if we are in the setter, that would make sense, but shouldn't this be moved to the second n_args ==2 section, and guard with if n_args == 1, the getter part?
There was a problem hiding this comment.
Or, value() is always a getter, and optionally a settter (if an arg is passed)?
There was a problem hiding this comment.
This is for the reset condition, right?
value(x) read+reset happens, and therefore edges are missed in reset boundary?
There was a problem hiding this comment.
There is a requirement from the micropython API that the setter function should also return the current counter value.
In this case here, the flow is as follows:
To Set: counter.value()
- Disable the counter
- Read the counter value
- Process the value
- Reset the Counter
- Clear IRQs
- Enable the Counter
- Return the read counter value
To Get: counter .value()
- Read the counter value
- Process the value
- Return the read counter value
There was a problem hiding this comment.
I get it, it was a bit confusing that setter() also should get.
Still, does the counter needs to be disabled and renabled() ? Not sufficient to set the new counter value after reading (all that atomically)?
There was a problem hiding this comment.
There is a risk for race condition at higher frequency of the input trigger pulses. Safer to disable, read and enable.
We have a atomic lock only for the read Cy_TCPWM_Counter_GetCounter().
Signed-off-by: NaveenChengappa-IFX <Naveen.Chandura@infineon.com>
Signed-off-by: NaveenChengappa-IFX <Naveen.Chandura@infineon.com>
Signed-off-by: NaveenChengappa-IFX <Naveen.Chandura@infineon.com>
Signed-off-by: NaveenChengappa-IFX <Naveen.Chandura@infineon.com>
Signed-off-by: NaveenChengappa-IFX <Naveen.Chandura@infineon.com>
jaenrig-ifx
left a comment
There was a problem hiding this comment.
Very good job!
I leave up to you, if you want to rework some of these different behavior for this PR. Otherwise, you can can create tickets for future enablement/consideration. 👍
Summary
Testing
HIL Testing carried out to test all features.
Trade-offs and Alternatives
Generative AI
I did not use generative AI tools when creating this PR.