How properly, under RTOS, to set up the highest priority non-maskable interrupt vector?
Posted: Fri Apr 19, 2024 8:56 pm
For example, in XTENSA ISA architecture there are several CCOMPARE registers, capable to generete an interrupt once CCOUNT (32bits hardware clock counter) matches the value in any CCOMPARE register. Since setup of an interrupt also requires interrupt priority level, the my goal is to set up that interupt with highest priority, so it can not be masked (disabled) or interrupted by any other interrrupt, because only one assembler instruction will be executed in that ISR handler and that should not impact on any other high priority interrupts. (actually 2 istructions, because, according to documentation, writting value back to CCOMPARE is required to clear that interrupt bit):
Is it possible to setup suc interrupt under RTOS application layer, or it requires RTOS kernel modification? Which of those CCOMPARE registers are reserved for RTOS (if any)?
I just want to extend that 32bit CCOUNT value to 64 bits. So value in CCOMPARE[1] (or any other available index not used by RTOS) would be 0, with meaning to generate a non-maskable interrupt when wrap-arround 32 bit boundary happens. In ISR it only needs to increment a value in some 32 bit variable in RAM which would be set up to 0 once chip is powered up. As such interrupt can occur only once in 17.89 seconds (it is approximatelly, for chip running at 240MHz) so its impact on the rest of any critical time routines can be ignored and it can be set up as non-maskable interrupt of the highest priority.
I am aware that reading of such "quasi" 64 bit value on 32 bit cores must be performed with two 32bit readings in assembly code, so some, let's say function GetCCount64 written in assembly, with intention to get that composed 64bit snapshot as accurate as possible and as sooner as possible, and still properly handle that wrap-arround problem could look like this:
(this is just pseudo code with crucial comments and timing examples of each assembler instruction)
With proper execution times of each instruction, we should be able to anulate all the clocks spent by calling this function and have exact snapshot of 64bit clock counter just right before the function is called. So you can write something like:
Snap variable will contain proper 64 bit snapshot clock value, just the same as it would be captured by the first instruction of your code by some hidden hardware mechanism. So "the first instruction of your code" executes at snapshot as there was no any function call before it. Offcorse, the real ticks are spent in that function but snap variable contains value as they are not. So you can add another snap1 varibale after your code and difference of those two will exact matches the ticks spent by your code only. Sure, that is just an illustrative example, becuse in RTOS environment you have to take into account possible switching contexts, interrupts happened during your code execution and so on.
However that same the function can be used to virtualy compensate ticks spent in any function (system or ISR routine(s) doesn't matter), but that is an another topic.
I also do not know why such a function is not already implemented although all XTENSA cores contain at least one (most of them contain 4) of CCOMPARE register. Such a function can be handy in many other cases (from debuging and fine-tuning, synchronizing of different ESPs, measuring performances and so on). Anyway, I can handle that assembler part for proper compensation, but can someone help in activating that hardware capability implemented in XTENSA cores in RTOS environment?
The CCOUNT register increments on every processor-clock cycle. When CCOUNT =
CCOMPARE, a TIMERINT interrupt request is generated. Although CCOUNT continues to increment and thus matches for only one cycle, the interrupt request is remembered until the interrupt is taken. In spite of this, timer interrupts are cleared by writing
CCOMPARE, not by writing INTCLEAR. Interrupt configuration determines the interrupt number and level. It is automatically an Internal interrupt type (the INTTYPE
configuration parameter, Table 4–70).
Is it possible to setup suc interrupt under RTOS application layer, or it requires RTOS kernel modification? Which of those CCOMPARE registers are reserved for RTOS (if any)?
I just want to extend that 32bit CCOUNT value to 64 bits. So value in CCOMPARE[1] (or any other available index not used by RTOS) would be 0, with meaning to generate a non-maskable interrupt when wrap-arround 32 bit boundary happens. In ISR it only needs to increment a value in some 32 bit variable in RAM which would be set up to 0 once chip is powered up. As such interrupt can occur only once in 17.89 seconds (it is approximatelly, for chip running at 240MHz) so its impact on the rest of any critical time routines can be ignored and it can be set up as non-maskable interrupt of the highest priority.
I am aware that reading of such "quasi" 64 bit value on 32 bit cores must be performed with two 32bit readings in assembly code, so some, let's say function GetCCount64 written in assembly, with intention to get that composed 64bit snapshot as accurate as possible and as sooner as possible, and still properly handle that wrap-arround problem could look like this:
(this is just pseudo code with crucial comments and timing examples of each assembler instruction)
Code: Select all
call to this function (lets say 3 clocks)
and assignment the result value to some 64bit variable (4 clocks)
uint64_t GetSnapshotCCount64(void){
1. DiasableInterrupts (lets say 10 clocks)
2. FlushTheCacheLines (lets say 5 clocks) // we do not want cache impacts on execution time
3. read HigherPart of CCOUNT in lets say R1 register //stored in our memory location and handled by our ISR (lets say 1 clock)
4. rsr.CCOUNT in some register lets say R0 // read the value of lower 32bit part (CCOUNT is special register so rsr instruction is required for reading of its content (lets say 2 clocks)
5. compare that value in R0 with 32 bit constant 0x00000003 // that is the amount of clock cycles spent from reading of higher part to reading of lower part. If value in R0 is less than that (means value in R0 is 0,1 or 2), we know a wrap-arround has just happened and read value in R1 belongs to previous wrap so we have to increase it by 1
6. Now we have 64 bit value in register pair R1 and R0 which represents proper 64 bit snapshot at the moment after execution of assembler instruction in step 4 and we want to compensate all clocks spent
in calling of our function and clocks of steps 1, 2, 3 and 4 so we will subtract 25 clocks from that 64 bit snapshot like uint64_t result = R1:R0 - 25 (because all clocks till step 5 sums to 25)
7. EnableInterrupts
8. return compensated 64 bit result
}
With proper execution times of each instruction, we should be able to anulate all the clocks spent by calling this function and have exact snapshot of 64bit clock counter just right before the function is called. So you can write something like:
Code: Select all
uint64_t snap = GetSnapshotCCount64();
the first instruction of your code
the second instruction of your code
....
uint64_t ticks_spent = GetSnapshotCCount64() - snap;
However that same the function can be used to virtualy compensate ticks spent in any function (system or ISR routine(s) doesn't matter), but that is an another topic.
I also do not know why such a function is not already implemented although all XTENSA cores contain at least one (most of them contain 4) of CCOMPARE register. Such a function can be handy in many other cases (from debuging and fine-tuning, synchronizing of different ESPs, measuring performances and so on). Anyway, I can handle that assembler part for proper compensation, but can someone help in activating that hardware capability implemented in XTENSA cores in RTOS environment?