[AVR/Attiny85] comp match skipped after OCR0A update
Quote from Coethium on September 5, 2024, 10:01 amGot an idea this night :
calcCounter() must calculate m_Counter only once when circTime has changed. Need to keep a circTime variable in the class/object.
At the begin of each "external" called function (read/write reg) : call calcCounter() before any other things (but maybe not for external clocked Timer ?)
I'll do some try and tests today.
Got an idea this night :
calcCounter() must calculate m_Counter only once when circTime has changed. Need to keep a circTime variable in the class/object.
At the begin of each "external" called function (read/write reg) : call calcCounter() before any other things (but maybe not for external clocked Timer ?)
I'll do some try and tests today.
Quote from arcachofo on September 5, 2024, 10:06 amcalcCounter() must calculate m_Counter only once when circTime has changed. Need to keep a circTime variable in the class/object.
Yes! that seems an excelent idea.
calcCounter() must calculate m_Counter only once when circTime has changed. Need to keep a circTime variable in the class/object.
Yes! that seems an excelent idea.
Quote from Coethium on September 5, 2024, 10:10 amI still don't understand why: "But as we update OCR0A, the new ovf shoud be about 1 cycle before (~259,xxx)."
In this log circTime with comma is about 1 timer clock tick (1,024 precisely) .
Before OCR0A update : current circTime is about 10 ; and next ovf Time will be ~260 ; so the next ovf is in ~250 circTime (ie. 250/1,024 = 244 Timer tick).
After decrementing OCR0A ; current circTime is almays about 10 ; but the next ovf must be before because we shortened the remaining period ; so ovf must be ~259
I still don't understand why: "But as we update OCR0A, the new ovf shoud be about 1 cycle before (~259,xxx)."
In this log circTime with comma is about 1 timer clock tick (1,024 precisely) .
Before OCR0A update : current circTime is about 10 ; and next ovf Time will be ~260 ; so the next ovf is in ~250 circTime (ie. 250/1,024 = 244 Timer tick).
After decrementing OCR0A ; current circTime is almays about 10 ; but the next ovf must be before because we shortened the remaining period ; so ovf must be ~259
Quote from Coethium on September 5, 2024, 12:29 pmSome news : making calculation only once when circTime has changed corrects the first sketch ; but not the second.
My hypothesis : i see some cycles (in circuit-unit) are calculated as a diff between Counter and Overflow value (in timer-unit) ; then converted into circuit-unit. When a timer value changes ; it could be at any time in circuitTime.
change new ovf time V +1cycle V _________________ _________________ _____| |______ ... ____| |____________ |< duration of >| ^ 1 timer tick wanted ovf time
I guess all internal values of the Timer and OcUnits have to be in circuit-unit, none in timer unit (or simply cached for reading) ; and all calculation (including differences ) done in circuit-units.
Register changes in timer-unit --> convert value in circuit-unit relative to the start of the counter changing time in circuit unit --> make calculations in circuit-unit, using internal values in circuit-unit (ovf, partial cycle, ...) --> when needed store modified registers in timer-unit (but TCNT0 ignored).
Seems to be a lot of work.
Some news : making calculation only once when circTime has changed corrects the first sketch ; but not the second.
My hypothesis : i see some cycles (in circuit-unit) are calculated as a diff between Counter and Overflow value (in timer-unit) ; then converted into circuit-unit. When a timer value changes ; it could be at any time in circuitTime.
change new ovf time
V +1cycle V
_________________ _________________
_____| |______ ... ____| |____________
|< duration of >| ^
1 timer tick wanted ovf time
I guess all internal values of the Timer and OcUnits have to be in circuit-unit, none in timer unit (or simply cached for reading) ; and all calculation (including differences ) done in circuit-units.
Register changes in timer-unit --> convert value in circuit-unit relative to the start of the counter changing time in circuit unit --> make calculations in circuit-unit, using internal values in circuit-unit (ovf, partial cycle, ...) --> when needed store modified registers in timer-unit (but TCNT0 ignored).
Seems to be a lot of work.
Quote from arcachofo on September 5, 2024, 1:04 pmAfter decrementing OCR0A ; current circTime is almays about 10 ; but the next ovf must be before because we shortened the remaining period ; so ovf must be ~259
Thanks, I think I kind of understand, but I haven't got into this yet
Quote from Coethium on September 5, 2024, 12:29 pmSome news : making calculation only once when circTime has changed corrects the first sketch ; but not the second.
My hypothesis : i see some cycles (in circuit-unit) are calculated as a diff between Counter and Overflow value (in timer-unit) ; then converted into circuit-unit. When a timer value changes ; it could be at any time in circuitTime.
change new ovf time V +1cycle V _________________ _________________ _____| |______ ... ____| |____________ |< duration of >| ^ 1 timer tick wanted ovf time
ArduinoI guess all internal values of the Timer and OcUnits have to be in circuit-unit, none in timer unit (or simply cached for reading) ; and all calculation (including differences ) done in circuit-units.
Register changes in timer-unit --> convert value in circuit-unit relative to the start of the counter changing time in circuit unit --> make calculations in circuit-unit, using internal values in circuit-unit (ovf, partial cycle, ...) --> when needed store modified registers in timer-unit (but TCNT0 ignored).
Seems to be a lot of work.
Not sure but I think that I see what you mean.
When calculating the current Timer counte value m_countVal, sometimes we are discarding some time here at McuTimer::calcCounter():
uint64_t cycles2Ovf = time2Ovf/m_scale;So indeed we are in the middle of the previous Timer tick than calculated...
Maybe we can have a look at the module: time2Ovf%m_scale, and substract 1 if module>0.Any way not 100% sure if I'm understanding.
Also scheduleEvents() is using whole cycles, which is wrong in these cases (maybe also use the module?).
We definetly need to do something here.
After decrementing OCR0A ; current circTime is almays about 10 ; but the next ovf must be before because we shortened the remaining period ; so ovf must be ~259
Thanks, I think I kind of understand, but I haven't got into this yet
Quote from Coethium on September 5, 2024, 12:29 pmSome news : making calculation only once when circTime has changed corrects the first sketch ; but not the second.
My hypothesis : i see some cycles (in circuit-unit) are calculated as a diff between Counter and Overflow value (in timer-unit) ; then converted into circuit-unit. When a timer value changes ; it could be at any time in circuitTime.
change new ovf time V +1cycle V _________________ _________________ _____| |______ ... ____| |____________ |< duration of >| ^ 1 timer tick wanted ovf time
ArduinoI guess all internal values of the Timer and OcUnits have to be in circuit-unit, none in timer unit (or simply cached for reading) ; and all calculation (including differences ) done in circuit-units.
Register changes in timer-unit --> convert value in circuit-unit relative to the start of the counter changing time in circuit unit --> make calculations in circuit-unit, using internal values in circuit-unit (ovf, partial cycle, ...) --> when needed store modified registers in timer-unit (but TCNT0 ignored).
Seems to be a lot of work.
Not sure but I think that I see what you mean.
When calculating the current Timer counte value m_countVal, sometimes we are discarding some time here at McuTimer::calcCounter():
uint64_t cycles2Ovf = time2Ovf/m_scale;
So indeed we are in the middle of the previous Timer tick than calculated...
Maybe we can have a look at the module: time2Ovf%m_scale, and substract 1 if module>0.
Any way not 100% sure if I'm understanding.
Also scheduleEvents() is using whole cycles, which is wrong in these cases (maybe also use the module?).
We definetly need to do something here.
Quote from Coethium on September 5, 2024, 3:41 pmYou pointed the modulo time2Ovf%m_scale ; this gave me the idea to store the offset between "now" and the theorical begin of the timer tick.
+ do not recalculate m_Counter within the same circTime.
Added a minus m_circTimeOffset when calculating cycles.
It seems to work (on the two sketches) !
I opened a PR, but i suggest you to not merge it before a careful reading.
You pointed the modulo time2Ovf%m_scale ; this gave me the idea to store the offset between "now" and the theorical begin of the timer tick.
+ do not recalculate m_Counter within the same circTime.
Added a minus m_circTimeOffset when calculating cycles.
It seems to work (on the two sketches) !
I opened a PR, but i suggest you to not merge it before a careful reading.
Quote from arcachofo on September 5, 2024, 4:06 pmGreat, seems good to me.
I merged and I'm doing some tests and try to find any possible problems.
Great, seems good to me.
I merged and I'm doing some tests and try to find any possible problems.
Quote from arcachofo on September 6, 2024, 5:07 amFixed some timing issues and changed some names to better fit it's purpose.
Did a bunch of tests, but more are needed.
I also want to use the new "test" capabilities of Oscilloscope and Logic Analyzer:
It can save readings for a period of time and on subsequent runs it will complain if timings don't match.
This way a circuit can be used as "test unit", saving readings when it is working correctly and then running it after some change/bugfix to detect regressions.
This still need some work and also some way to run a batch of "test circuits" in one go.EDIT:
Forgot to mention: I disabled AvrIntOsc, it is messing the MCU frequency in some cases (also removed McuPrescaled subclassing).
I will get into this later today.
Fixed some timing issues and changed some names to better fit it's purpose.
Did a bunch of tests, but more are needed.
I also want to use the new "test" capabilities of Oscilloscope and Logic Analyzer:
It can save readings for a period of time and on subsequent runs it will complain if timings don't match.
This way a circuit can be used as "test unit", saving readings when it is working correctly and then running it after some change/bugfix to detect regressions.
This still need some work and also some way to run a batch of "test circuits" in one go.
EDIT:
Forgot to mention: I disabled AvrIntOsc, it is messing the MCU frequency in some cases (also removed McuPrescaled subclassing).
I will get into this later today.
Quote from Coethium on September 6, 2024, 12:32 pmI thought some tests to do :
- what if Clock Select if modified when Timer is running ? Are events well recalculated ?
- this include the case where CS is disabled : CS=xx --> CS=0 (disable clock) --> CS=yy (reenable)
Your idea of test capability seems very good ! Later it could be extended to monitor registers and/or specific RAM location (this is the same) to complain when values don't match over time with the "test circuit".
For AvrItnOsc : hmm you're right, changing mcu frequency affects timers ; so events have to be recalculated.
EDIT:
idea : in McuPrescaled add a rescheduleEvents(float ratio=1.0) method ; which do nothing (abstract ?) ; when prescaler change ; it call this method with the good ratio.
In McuTimer redef this method which only recalculate the next event (something like neweventtime = nowtime+(oldeveventtime-nowtime)*ratio) ; and maybe update some variables ; of course it also call an OcUnit equivalent method.
In the case of McuIntOsc, when always a child of McuPrescaled ; its rescheduleEvents() method have to call rescheduleEvents() of all of its timers.
I thought some tests to do :
- what if Clock Select if modified when Timer is running ? Are events well recalculated ?
- this include the case where CS is disabled : CS=xx --> CS=0 (disable clock) --> CS=yy (reenable)
Your idea of test capability seems very good ! Later it could be extended to monitor registers and/or specific RAM location (this is the same) to complain when values don't match over time with the "test circuit".
For AvrItnOsc : hmm you're right, changing mcu frequency affects timers ; so events have to be recalculated.
EDIT:
idea : in McuPrescaled add a rescheduleEvents(float ratio=1.0) method ; which do nothing (abstract ?) ; when prescaler change ; it call this method with the good ratio.
In McuTimer redef this method which only recalculate the next event (something like neweventtime = nowtime+(oldeveventtime-nowtime)*ratio) ; and maybe update some variables ; of course it also call an OcUnit equivalent method.
In the case of McuIntOsc, when always a child of McuPrescaled ; its rescheduleEvents() method have to call rescheduleEvents() of all of its timers.
Quote from arcachofo on September 6, 2024, 12:46 pmYes, I still need to refactor how the MCU frequency works to support dynamic changes.
Then add some "callbacks" for the Timers to reconfigure.About unit test for RAM it is also in my mind, but this one doesn't seem so easy.
What is currently implemented is Oscope and LA is quite simple.
There is also a simple "Test Unit" component for Combinational Logic that can be used in some cases.But we desperately need to implement this, testing is quite time consuming.
Yes, I still need to refactor how the MCU frequency works to support dynamic changes.
Then add some "callbacks" for the Timers to reconfigure.
About unit test for RAM it is also in my mind, but this one doesn't seem so easy.
What is currently implemented is Oscope and LA is quite simple.
There is also a simple "Test Unit" component for Combinational Logic that can be used in some cases.
But we desperately need to implement this, testing is quite time consuming.