Nios II VIC for interrupts

mark1122 wrote on Wednesday, March 20, 2019:

Hi, the current freeRTOS only supports IIC for NiosII. VIC is about 4x faster. I have over 10 interrupts in my design and I really need the VIC for performance. I saw the feature request #115 created 1 year and 9 months ago. Could someone give me a hint on how to implement it? I am sure someone has done it.
My understanding of IIC is that all interrupts(hardware + softare) go through the exception vector(usually address 0x20 or anything defined in Qsys). Task switch inside a tick is through a software “trap”. freeRTOS stores the TCB+stack for each task in different regions. How do people handle this?

Thanks,
Mark

mark1122 wrote on Wednesday, March 20, 2019:

I think the software “trap” for task switch can use a hardware interrupt to. With the VIC shadow register sets, it should speed up task swiching.

mark1122 wrote on Wednesday, March 20, 2019:

Co-operative scheduling(configUSE_PREEMPTION=0) works with VIC :-), still need to get the hardware interrup handler working.

rtel wrote on Wednesday, March 20, 2019:

Hi Mark - thanks for the info. I need to get the Interactive site
working again so we can share.

mark1122 wrote on Wednesday, March 20, 2019:

Hi Richard, the reason I use co-operative scheduling is that I only need to deal with the software “trap” exception/scheduler and figure out how to use the VIC instead(no context switching in the timer interrupt for now), so I am aiming at one target instead of 2 :-). I am going to report back when I found something.

ripperle wrote on Friday, June 14, 2019:

Hi,

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, FreeRTOS - The Free RTOS configuration constants and configuration options - FREE Open Source RTOS for small real time embedded systems) 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
  • VIC
  • 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 */
#include "altera_vic_funnel.h"

    .section .text, "xa"
    .align 2
    .globl UC_VIC_VECTOR_TABLE
UC_VIC_VECTOR_TABLE:
     #ALT_NORMAL_NON_PREEMPTIVE_INTERRUPT    16
     jmpi isr_handler
     .word 0x0
     .word 0x0
     .word 0x0
     ALT_SHADOW_NON_PREEMPTIVE_INTERRUPT    16
     ALT_SHADOW_NON_PREEMPTIVE_INTERRUPT    16
     ALT_SHADOW_NON_PREEMPTIVE_INTERRUPT    16
     ALT_SHADOW_NON_PREEMPTIVE_INTERRUPT    16
     ALT_SHADOW_NON_PREEMPTIVE_INTERRUPT    16
     ALT_SHADOW_NON_PREEMPTIVE_INTERRUPT    16
     ALT_SHADOW_NON_PREEMPTIVE_INTERRUPT    16
  • the context switch (written in assembler) is different in contrast to the existing port for the NIIS2 IIC for these reasons:
  1. 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…
  2. 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:

  1. Only one VIC is implemented (no daisy chain of multiple VIC).
  2. 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?
  3. 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.
  4. 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…

Regards,
Andy

I fixed this implementation issues (no problem anymore!).
The port will now use the “STATUS.IL” field of the CPU STATUS register to enable and disable the IRQs below or equal configMAX_SYSCALL_INTERRUPT_PRIORITY.
Because the status register is saved at context switch, the IRQ enable state is also saved as a part of the task context…

The .zip file contains the sources. Copy them to FreeRTOS/Source/portable/GCC/NiosII_EIC.

I am also porting the FreeRTOS TCP/IP stack at the moment…

Regards,
Andy

Nios2_EIC.zip (7.6 KB)