How do you sort a list of unsigned integers that rollover?
tonyp12
Posts: 1,951
I have this 24bit unsigned Counter (RTC) with a 24bit CompareRegister, they both use uint32_t (upper 8bit is unused)
After a IRQ hits, I want to find the next closest value from 32 possible values.
As example with keep it as it 4bit 0-15 with four in a list:
___________________________________________________________________________________________
Say IRQ hit at 5, it now need to find the nearest
values in list is 5, 0, 8,14
It will signal the task that asked for the 5 match and then it needs to find 8 in list to prepare next IRQ
___________________________________________________________________________________________
IRQ now hit at 8, it now need to find the nearest
values in list is now 10, 0, 8,14 (the first thread added 5 to its match)
So it needs to find 10.
___________________________________________________________________________________________
IRQ now hit at 10, it now need to find the nearest
values in list is now 10, 0, 0,14 (the third thread added 8 to its match)
So it needs to find 14.
___________________________________________________________________________________________
IRQ now hit at 14, it now need to find the nearest
values in list is now 3, 0, 0,14 (the first thread added 8 to its match)
So it needs to find both zeros, two of the same values should ORa the eventflags.
If a thread ask for lower value than next prepared IRQ, it will just over write CompareRegister and change eventflag.
If value is same it will just ORa itself to pending eventflags.
I use C, but SPIN example should be easy to translate.
Maybe two pass?, but I want it to be fast though.
First pass if no values above current counter, then do a seconds pass treating values low-to-high just like normal sorting.
After a IRQ hits, I want to find the next closest value from 32 possible values.
As example with keep it as it 4bit 0-15 with four in a list:
___________________________________________________________________________________________
Say IRQ hit at 5, it now need to find the nearest
values in list is 5, 0, 8,14
It will signal the task that asked for the 5 match and then it needs to find 8 in list to prepare next IRQ
___________________________________________________________________________________________
IRQ now hit at 8, it now need to find the nearest
values in list is now 10, 0, 8,14 (the first thread added 5 to its match)
So it needs to find 10.
___________________________________________________________________________________________
IRQ now hit at 10, it now need to find the nearest
values in list is now 10, 0, 0,14 (the third thread added 8 to its match)
So it needs to find 14.
___________________________________________________________________________________________
IRQ now hit at 14, it now need to find the nearest
values in list is now 3, 0, 0,14 (the first thread added 8 to its match)
So it needs to find both zeros, two of the same values should ORa the eventflags.
If a thread ask for lower value than next prepared IRQ, it will just over write CompareRegister and change eventflag.
If value is same it will just ORa itself to pending eventflags.
I use C, but SPIN example should be easy to translate.
Maybe two pass?, but I want it to be fast though.
First pass if no values above current counter, then do a seconds pass treating values low-to-high just like normal sorting.
Comments
I divide the freq to get 0.97ms rtc steps, so rollover only happens every 4hrs.
Is this P1 or P2?
Math is math on any platform.
I endup looking for lowest & still higher than last (e.g current) counter value. (not final, hardcoded unrolled loop for testing) I do this again when counter is at zero, when rollover values will fall in place.
This is a trick question right?
It's not possible to sort things unless there is a concept of order: "greater than", "equal to", "less than".
If N + 1 turns out to be less than N, due to rollover, overflow, whatever, then you have a problem.
What actually are you wanting to do?
I'm creating a mailbox cooperative-multitasking OS.
When a tasks-statemachine is done, it sets its outbox with a time it want to be invoked again.
Turning off HighFreq Osc as often as possible, even for short sleeps like 2ms, saving battery.
There is no greatest common denominator, any task can sleep any length (in my case 1ms to 4hours)
The method I've historically used is to make everything relative. It takes some more adds and subtracts but you get way less conditional execution.
To do this there is always a local reference position upon which the calculation is based. The calculation is done with the reference treated as zero and only at the end is it added on to provide the absolute position.
For a straight threshold compare like, you only need subtract the reference before doing the compare. In C that becomes: In this case the threshold is your reference.
NOTE: Be careful not to let absolutes creep into the comparison stage.
The three mytask1.outbox-rtmatch should be stored in a register for quick access,
as ARM is load-and-store so better hope the compiler sees that, in high optimization mode at least.
Could always: uint32_t myvalue =mytask1.outbox-rtmatch; as local var is always trying to use a register first.
PS: Ditch all the unsigned variables. With all the calculations centred around zero you'll be getting negative results as much as positives.
That example doesn't show the sample being truncated because once its a 16-bit integer then that part is done. The truncation depends on how the hardware is being read, and what datatypes are involved.
I have solved this problem for my Propeller multi-tasking co-operative scheduler in the following manner, and is Propeller specific. Not sure if it could be effectively modified for other architectures.
I keep an arbitrarily long sorted list of delta time that each task wants to run, relative to the next task. These times can be 0 to 31 bit ticks long, so the longest delay may not exceed 26 seconds at 80 MHz.
A WAITCNT is executed until the-next-to-run thread's time match occurs, and that thread then continues from where it left off last time it ran. On completing its short task (NO loops or WAITs permitted) it calculates the delta match value for its next run time. It then sorts though the list sequentially until it finds a delta time that is larger than itself, or the end of the list, each time reducing its own delta time by the value of each entry it compares to and skips. The scheduler then inserts its remaining delta value at that point in the list. This keeps the list sorted. Then the scheduler takes the bottom entry (lowest delta value) off the list and adds that to the WAITCNT match register, thereby setting the time for the next to run.
By using delta times of 31 bits or less, there are no overflows, so comparisons become easy. I tried so many other ways of doing this, all to no avail, and have had perfect luck with this simple approach.
Any (practical) number of threads, each with 0 to 2^31 ticks, hence a resolution of 12.5 nS. In the case of a zero delta, when two threads calculate to run at the same time, the thread is already "behind", and runs immediately.
Just for fun, I've had 256 LED flash threads running in a single Cog.
Cheers,
Peter (pjv)
The problem is of course if that value rolled over.
4bit example:
current time is 14, and it wants to comeback at 15, another task have already set CompareRegister to 1,
15 is not lower than 1, but is sooner than 1.
Having a flag set that detected if sort routine set Comparegister to a values that rolled over?
Or
As rtc is 24bit only, I could keep values flowing over in to 25bit and then the rollover IRQ hits it could then ANDing all values with a 24bit mask.
But if a MCU series comes with a 32bit timer I could only use 31bit.
Mike
id1: 1ms to 4.5hrs
id2: date in seconds since 2000, that the overflow-irq check if within the next 4.5hrs and change it to id1.
So total range is 1ms to year2138.
example of usage: