ripperle wrote on Friday, June 14, 2019:
i started to write a port for the NIOS2 with VIC (vectored interrupt controller) and the basic features (preemptive context switching with nested interrupt model, https://www.freertos.org/a00110.html#kernel_priority) already seems to work.
Here the Hardware description for the Intel (aka Altera) Qsys System:
- NIOS 2 Processor (NIOS II/f with shadow registers) @100MHz
- Interval Timer (@ 1ms) for the RTOS tick
- some other Timers and custum components with high priority interrupt and minimal latency requirements
- and of course some RAM (onchip RAM, ca. 40k)
Settings in the BSP Project / Editor:
hal.linker.enable_exception_stack set to false (no performance improvement with VIC)
hal.linker.enable_interrupt_stack set to false (no performance improvement with VIC)
- Timer for RTOS Tick (IRQ0) set to priority 1 (RIL = 1, lowest priority)
- IRQ1 to IRQ7 set to higher prioritys, from RIL=9 to RIL=15 which is the high priority interrupt
- The IRQ0 (RTOS Tick Timer) runs in the register set 0 (RSS=0) which is “normal” register set. This is set, because the context switch has to be customized for FreeRTOS anyway, and there will be no performance improvements with shadow registers
- IRQ1 to IRQ7 are configured to use there own shadow register set (IRQ1=RRS2, IRQ2=RRS3, …, IRQ7=RRS8)
altera_vic_driver.enable_preemption set to true
altera_vic_driver.enable_preemption_into_new_register_set set to true
Changes to the existing port for the NIOSII with IIC (internal interrupt controller):
- with an VIC there are seprerated interrupt entry addresses for each interrupt. These addresses are stored in a vector table.
The altera VIC driver of the BSP will create these table in the file altera_uC_VIC_vector_tbl.S when generating the BSP. It places the corresponding interrupt funnels at these addresses. The funnel normally will save and restore CPU context and call the ISR, definied by the “alt_ic_isr_register()” function. With shadow registers for each ISR its unnessaccry to save and restore context. So the funnel will be very slim.
Instead of using the altera isr funnel for the first interrupt (ISR0 aka RTOS Tick) I jump to the context switch implementation of the RTOS port:
/* Include funnel macros header file */
.section .text, "xa"
- the context switch (written in assembler) is different in contrast to the existing port for the NIIS2 IIC for these reasons:
- Separate entry point for non interrupt exceptions. This is important because the port_YIELD macro of FreeRTOS will execute the “trap” instruction. Other exceptions has to be handled in an other way…
- because of the nested interrupt model (configKERNEL_INTERRUPT_PRIORITY and configMAX_SYSCALL_INTERRUPT_PRIORITY are defined) the interrupt enable state of the current task has to be saved and restored inside the context switch. Also the global interrupt enable bit (PIE of the CPU status register) has to be set at the beginning of the context switch to allow interrupts with priorities above configMAX_SYSCALL_INTERRUPT_PRIORITY to execute (with there own shadow registers).
RE: Question about taskYIELD()
Posted by Richard on August 3, 2007
It is the intended behaviour that the context switch occurs within the critical section - this allows the use of the scheduler locking mechanism. Each task should maintain its own interrupt status. Therefore a task that has interrupts disabled can call taskYIELD(), and switch to a task that may have interrupts enabled. When the original task next runs it starts with the interrupts in the state in which it expects to find them - disabled. Therefore this mechanism does not mean that interrupts remain disabled until the task executes again.
Remaining issues, limitations and questions:
- Only one VIC is implemented (no daisy chain of multiple VIC).
- I use the vTaskSetThreadLocalStoragePointer mechanism to store the interrupt state in the portDISABLE_INTERRUPTS macro. I dont like this method because the state can be corrupted if someone call the API with index 0 from user space. Is there a better way or place to store this information?
- In the VIC the priority of each interrupt (e.g. IRQ0 to IRQ7) can be configured individually. So for example IRQ3 could have a higher priority than IRQ4. I save a “irq_disable_mask” variable initialized in the xPortStartScheduler functions (set corresponding bit of the variable if RIL <= configMAX_SYSCALL_INTERRUPT_PRIORITY). At context switch I can disable necessary interrupts instantly with this mask and the INT_ENABLE_CLR register of the VIC. Therefore its not allowed to change the priority of any interrupt after starting the scheduler.
The altera_uC_VIC_vector_tbl.S is modified by hand and will be overwritten when the BSP is generated. I don’t find another way to use the custom ISR written in assembler beside the Altera VIC driver library. This topic is described in the “Embedded Peripherals IP User Guide” from Intel (Altera), chapter 37.7.4 “Positioning the ISR in Vector Table”:
Positioning an ISR in a vector table is an advanced and error-prone technique, notdirectly supported by the HAL. You must exercise great caution to ensure that the ISRcode fits in the vector table entry.
The VIC Table-Resident example requires modifying a BSP-generated file,altera_vic1_vector_tbl.S. If you regenerate the BSP after making these modifications, the Nios II Software Build Tools regenerate altera_vic1_vector_tbl.S,and your changes are overwritten.
As a workaround i just created a copy of the altera_uC_VIC_vector_tbl.S with the modified content and copy them as a “BUILD_PRE_PROCESS” target in the correct folder.
I am grateful for any comments or improvements from you…