Closed sambhurst closed 9 months ago
Would really like to have this feature, STM32/GD32 now need to rely on hacks to use shared IRQs. What is the reason for Allow up to three ISRs to share one interrupt line
?
Would really like to have this feature, STM32/GD32 now need to rely on hacks to use shared IRQs. What is the reason for
Allow up to three ISRs to share one interrupt line
?
I was thinking that calling too many functions in an ISR would bog down the system and I haven't seen very many MCUs that have more than three peripherals sharing one interrupt line but the short answer is, the number was chosen arbitrarily.
I like that approach of calling all the registered handlers from the isr in the shared_isr section, without requiring a separate driver. In the case of STM32G0 quite a lot interrupts can overlap(UART, EXTI, DMA) so I wouldn't limit it to 3.
Do you plan to open a PR implementing this proposal or would you rather gather more feedback before.
I like that approach of calling all the registered handlers from the isr in the shared_isr section, without requiring a separate driver. In the case of STM32G0 quite a lot interrupts can overlap(UART, EXTI, DMA) so I wouldn't limit it to 3.
Do you plan to open a PR implementing this proposal or would you rather gather more feedback before.
I think the only issue is the number of shared interrupts allowed. I'll bump it up to 4 and open a PR. I thought about using a Kconfig but I didn't know how to pass the option to the python script.
How does this differ from what is done in drivers/interrupt_controller/Kconfig.shared_irq & drivers/interrupt_controller/intc_shared_irq.c?
How does this differ from what is done in drivers/interrupt_controller/Kconfig.shared_irq & drivers/interrupt_controller/intc_shared_irq.c?
To be honest, I was unaware of this implementation and it seems that no projects are using it, other than drivers/ethernet/eth_smsc911x.c including the header. The shared_irq implementation doesn't seem very compatible with the current drivers and would require some code changes.
This pull request is more efficient when calling the shared ISR handlers.
How does this differ from what is done in drivers/interrupt_controller/Kconfig.shared_irq & drivers/interrupt_controller/intc_shared_irq.c?
I have tried this approach in the past. The int_shared_irq controller is based on multilevel interrupts, which are not supported on aarch32.
I've received some useful comments on the PR but I think the discussion should move back to the issue. In the PR, I've implemented two different versions that each have pros and cons. I'd like to list them here so we can pick one.
1) Only ISRs created with IRQ_DIRECT_CONNECT can be shared. Pros: Very easy to implement, no C or ASM code needs to change, and any number of shared ISRs. Cons: No arguments can be passed to the ISRs. Example: Generated isr_tables.c looks as follows:
/ Shared Prototypes / void attribute((section(".shared_isr")))shared_isr_20(void)
/ IRQ_DIRECT_CONNECT table / uintptr_t __irq_vector_table _irq_vector_table[102] = { ... ((uintptr_t)&shared_isr_20), .... };
/ IRQ_CONNECT table / struct _isr_table_entry __sw_isr_table _sw_isr_table[102] = { ... };
void attribute((section(".shared_isr")))shared_isr_20(void){ ((DIRECT_ISR)0x8000549)(); ((DIRECT_ISR)0x8000525)(); }
2) Allow ISRs created with IRQ_CONNECT to share IRQ lines. Pros: Arguments can be passed to ISRs. Cons: Not so easy to implement, C and ASM code needs to change, Flash memory will be wasted on unused shared ISR place holders. See below: Example: Generated isr_tables.c looks as follows:
/ IRQ_DIRECT_CONNECT table / uintptr_t __irq_vector_table _irq_vector_table[102] = { ... };
/ IRQ_CONNECT table / struct _isr_table_entry __sw_isr_table _sw_isr_table[102][SOME_NUMBER_OF_SHARED_ISRS] = { ... { {(const void )0x8009824, ((ISR)0x8000549)}, {(const void )0x8009734, ((ISR)0x8000525)}, {0,0} }, ... };
Each entry in the sw_isr_table is a NULL terminated list of shared interrupts with corresponding arguments. The ASM code needs to iterate through the list, calling each ISR in turn, until the NULL terminator is reached. Or the isr_table_entry structure can be changed to include the number of shared ISRs to iterate over. This approach wastes memory because this is a 2 dimensional array and not all entries will be used.
Or there is another way that I've yet to consider.
I haven't gone into much detail on how ISR tables are generated, so please ignore the following if it doesn't make sense. In case of shared IRQs, would it be possible to autogenerate a function stub that calls the N attached routines with the right parameter? Then the table would just point to such function stub. Non-shared IRQs would just call the original function.
I haven't gone into much detail on how ISR tables are generated, so please ignore the following if it doesn't make sense. In case of shared IRQs, would it be possible to autogenerate a function stub that calls the N attached routines with the right parameter? Then the table would just point to such function stub. Non-shared IRQs would just call the original function.
@gmarull That makes sense and was one of the approaches I tried. I stored the arguments in flash but then I needed to rewrite the arm/core/aarch32/isr_wrapper.S to access the arguments and call the ISRs. I thought this change would be to invasive and opted for the two approaches listed above. But maybe I should have listed this one too.
@gmarull After second thought, maybe I can store the arguments contiguously in flash and use the isr_wrapper.S to call the stub ISR along with a pointer to the list of arguments. Then the stub ISR can pass the appropriate arguments to the N attached routines.
Zephyr now supports shared interrupts on the architecture level: https://github.com/zephyrproject-rtos/zephyr/pull/61422
Introduction
Allow up to three Interrupt Service Routines (ISRs) to share one interrupt line.
Problem description
Many MCUs have peripherals that share interrupt lines. For example, the USB, UCPD1, and UCPD2 peripherals on the STM32G0B0x1 share interrupt 8. Currently no practical application can use those peripherals together without circumventing the Zephyr interrupt system or writing a custom device driver.
Proposed change
Interrupts are registered with a macro similar to
IRQ_DIRECT_CONNECT
that’s namedIRQ_SHARE_CONNECT
. Allow up to three ISRs to share one interrupt line. Only one of the user ISRs should service the interrupt, unless the interrupt is pending due to more than one source.Detailed RFC
Create a new flag,
ISR_FLAG_SHARE
, that’s passed toZ_ISR_DECLARE
.Add a new section to the linker script:
When the
gen_isr_tables.py
script executes and detects that theISR_FLAG_SHARE
flag is set, it generates a shared ISR that calls the user ISRs in sequence. The generated ISR is placed in the.shared_isr
section.For example:
Resulting
build/zephyr/isr_tables.c
generated bygen_isr_tables.py
:I have only tested this on the Cortex M architecture.