DUE TO SPAM, SIGN-UP IS DISABLED. Goto Selfserve wiki signup and request an account.
Smaller Vector Tables
One of the largest OS data structures is the vector table, {{Wiki Markup g_irqvector\[]}}. This is the table that holds the vector information when {{irq_attach()}} is called and used to dispatch interrupts by {{irq_dispatch()}}. Recent changes have made that table even larger, for 32-bit arm the size of that table is given by:
| Code Block |
|---|
nbytes = number_of_interrupts * (2 * sizeof(void *)) |
...
For a 32-bit ARM like the STM32 with, say, 100 interrupt vectors, this size would be 800 bytes of memory. That is not a lot for high-end MCUs with a lot of RAM memory, but could be a show stopper for MCUs with minimal RAM.unmigrated-wiki-markup
Two approaches for reducing the size of the vector tables are described below. Both depend on the fact that not all interrupts are used on a given MCU. Most of the time, the majority of entries in {{g_irqvector\[]}} are zero because only a small number of interrupts are actually attached and enabled by the application. If you know that certain IRQ numbers are not going to be used, then it is possible to filter those out and reduce the size to the number of supported interrupts.
For example, if the actual number of interrupts used were 20, the the above requirement would go from 800 bytes to 160 bytes.
Software IRQ Remapping
...
_\[On March 3, 2017, support for this "Software IRQ Remapping" as included in the NuttX repository.]_unmigrated-wiki-markup
One of the simplest way of reducing the size of {{g_irqvector\[]]}} would be to remap the large set of physcial interrupt vectors into a much small set of interrupts that are actually used. For the sake of discussion, let's imagine two new configuration settings:
CONFIG_ARCH_MINIMAL_VECTORTABLE: Enables IRQ mappingCONFIG_ARCH_NUSER_INTERRUPTS: The number of IRQs after mapping.
...
| Code Block |
|---|
#ifdef CONFIG_ARCH_MINIMAL_VECTORTABLE struct irq_info_s g_irqvector[CONFIG_ARCH_NUSER_INTERRUPTS]; #else struct irq_info_s g_irqvector[NR_IRQS]; #endif |
...
The {{g_irqvector\[]}} table is accessed in only three places:
irq_attach()
irq_attach() receives the physical vector number along with the information needed later to dispatch interrupts:
...
| Code Block |
|---|
#ifdef CONFIG_ARCH_MINIMAL_VECTORTABLE int ndx = g_irqmap[irq]; #else int ndx = irq; #endif |
...
where {{up_mapirq\[]}} is an array indexed by the physical interrupt vector number and contains the new, mapped interrupt vector table index. This array must be provided by platform-specific code.unmigrated-wiki-markup
{{irq_attach()}} would this use this index to set the g_irqvector\[].
| Code Block |
|---|
g_irqvector[ndx].handler = isr; g_irqvector[ndx].arg = arg; |
irq_dispatch()
irq_dispatch() is called by MCU logic when an interrupt is received:
...
| Code Block |
|---|
vector = g_irqvector[ndx].handler; arg = g_irqvector[ndx].arg; |
| Code Block |
vector(irq, context, arg); |
irq_initialize()
...
{{irq_initialize()}}: simply set the {{g_irqvector\[]}} table a known state on power-up. It would only have to distinquish the difference in sizes.
| Code Block |
|---|
#ifdef CONFIG_ARCH_MINIMAL_VECTORTABLE # define TAB_SIZE CONFIG_ARCH_NUSER_INTERRUPTS #else # define TAB_SIZE NR_IRQS #endif |
| Code Block |
for (i = 0; i < TAB_SIZE; i++) |
...
g_mapirq
...
[]
...
An implementation of up_mapirq() might be something like:
| Code Block |
|---|
#include <nuttx/irq.h> |
| Code Block |
const irq_mapped_t g_irqmap[NR_IRQS] =
{
... IRQ to index mapping values ...
};
|
...
{{g_irqmap\[]}} is a array of mapped irq table indices. It contains the mapped index value and is itself indexed by the physical interrupt vector number. It provides an {{irq_mapped_t}} value in the range of 0 to {{ CONFIG_ARCH_NUSER_INTERRUPTS }} that is the new, mapped index into the vector table. Unsupported IRQs would simply map to an out of range value like {{ IRQMAPPED_MAX}}. So, for example, if {{g_irqmap\[37]
...
== 24, then the hardware interrupt vector 37 will be mapped to the interrupt vector table at index 24. if g_irqmap[42] == IRQMAPPED_MAX, then hardware interrupt vector 42 is not used and if it occurs will result in an unexpected interrupt crash.
Hardware Vector Remapping
...
_\[This technical approach is discussed here but is discouraged because of technical "Complications" and "Dubious Performance Improvements" discussed at the end of this section.]_
Most ARMv7-M architectures support two mechanism for handling interrupts:
...
REVISIT: This needs to be updated. Neither the xyz_vector.h files nor the stm32_vectors.S exist in the current realization. This has all been replaced with the common vector handling at arch/arm/src/armv7-m.
Vector Definitions
In arch/arm/src/stm32/gnu/stm32_vector.S, notice that the xyz_vector.h file will be included twice. Before each inclusion, the macros VECTOR and UNUSED are defined.
The first time that xyz_vector.h included, it defines the hardware vector table. The hardware vector table consists of NR_IRQS 32-bit addresses in an array. This is accomplished by setting:
| Code Block |
|---|
#undef VECTOR #define VECTOR(l,i) .word l |
| Code Block |
#undef UNUSED #define UNUSED(i) .word stm32_reserved |
...
These are the settings for vector 53, 54, and 55, respectively. The entire vector table would be populated in this way. stm32_reserved, if called would result in an "unexpected ISR" crash. stm32_usart1, if called will process the USART1 interrupt normally as we will see below.
Interrupt Handler Definitions
in the vector table, all of the valid vectors are set to the address of a handler function. All unused vectors are force to vector to stm32_reserved. Currently, only vectors that are not supported by the hardware are marked UNUSED, but you can mark any vector UNUSED in order to eliminate it.
...
These are the valus of UNUSED and VECTOR macros on the second time the xzy_vector.h is included by stm32_vectors.S:
| Code Block |
|---|
.macro HANDLER, label, irqno .thumb_func \label: mov r0, #\irqno b exception_common .endm |
| Code Block |
#undef VECTOR #define VECTOR(l,i) HANDLER l, i |
| Code Block |
#undef UNUSED #define UNUSED(i) |
In the above USART1 example, a single handler would be generated that will provide the IRQ number 12. Remember that 12 is the expansion of the macro STM32_IRQ_USART1 that is provided in the arch/arm/include/stm32/xyz_irq.h header file:
...
Now, when vector 16+37 occurs it is mapped to IRQ 12 with no significant software overhead.
A Complication
A complication in the above logic has been noted by David Sidrane: When we access the NVIC in stm32_irq.c in order to enable and disable interrupts, the logic requires the physical vector number in order to select the NVIC register and the bit(s) the modify in the NVIC register.
...
| Code Block |
|---|
... VECTOR(stm32_usart1, STM32_IRQ_USART1 << 8 | STM32_INDEX_USART1) UNUSED(0) UNUSED(0) ... |
...
The {{ STM32_INDEX_USART1 }} would have the value 12 and STM32_IRQ_USART1 would be as before (53). This encoded value would be received by {{irq_dispatch()}} and it would decode both the index and the physical vector number. It would use the index to look up in the {{g_irqvector\[]}} table but would pass the physical vector number to the interrupt handler as the IRQ number.
A lookup would still be required in irq_attach() in order to convert the physical vector number back to an index (100 uint8_t entries in our example). So some lookup is unavoidable.
Based upon these analysis, my recommendation is that we do not consider the second option any further. The first option is cleaner, more portable, and generally preferable.is well worth that.
Dubious Performance Improvements
The intent of this second option was to provide a higher performance mapping of physical interrupt vectors to IRQ numbers compared to the pure software mapping of option 1. However, in order to implement this approach, we had to use the less efficient, non-common vector handling logic. That logic is not terribly less efficient, the cost is probably only a 16 bit load immediate instruction and branch to another location in FLASH (which will cause the CPU pipeline to be flushed).
...
Because of this and because of the simplicity of the first option, I see no reason to support or consider this second option any further.
Complexity and Generalizability
Option 2 is overly complex; it depends on a deep understanding on how the MCU interrupt logic works and on a high level of Thumb assembly language skills.
...