Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

drivers: intc: renesas_ra: Handle interrupts based on event numbers #75946

Open
wants to merge 12 commits into
base: main
Choose a base branch
from

Conversation

soburi
Copy link
Member

@soburi soburi commented Jul 16, 2024

The Renesas RA interrupt controller does not correspond to an interrupt number and function but is configurable by the user.
Zephyr's isr_table is designed under the assumption that interrupt numbers are assigned to specific functions, making this difficult to support.
We will extend gen_isr_table.py to create a table by specifying the function(event) number instead of the interrupt number.

When creating a table with function(event) numbers, interrupt handlers are assigned starting from interrupt number 0 in the order they are found during linking. At this time, it will also create a table that allows us to reference the assigned interrupt number from the function(event) number.
This is similar behavior as Renesas FSP's code generator.

By receiving event numbers instead of IRQ numbers and converting them internally to IRQ numbers, a unified interface based on event numbers can be created.
That is, it make API calls using event numbers rather than raw interrupt numbers.

This allows interrupt definitions in the device tree to also be described semantically by event numbers.

Related to #75440

@soburi soburi added the platform: Renesas RA Renesas Electronics Corporation, RA label Jul 16, 2024
@soburi soburi force-pushed the ra_icu_isr_table branch 4 times, most recently from d52ac91 to dcd9671 Compare July 17, 2024 01:09
@soburi soburi marked this pull request as ready for review July 17, 2024 01:23
@zephyrbot zephyrbot added area: UART Universal Asynchronous Receiver-Transmitter area: GPIO area: Architectures area: Build System area: Interrupt Controller area: Devicetree Binding PR modifies or adds a Device Tree binding area: ARM ARM (32-bit) Architecture platforms: Renesas RA labels Jul 17, 2024
@bjarki-andreasen
Copy link
Collaborator

Adding a DNM while I review

Copy link
Collaborator

@bjarki-andreasen bjarki-andreasen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR seems to be solving mapping from the interrupt line number of the UIC to the interrupt line number of the NVIC. This is typically done using interrupt maps in the devicetree. Has this been considered as an option? It seems quite extreme to rewrite the vector generation table and IRQ macros just to introduce this mapping.

See section 2.4.3 of https://github.com/devicetree-org/devicetree-specification/releases/tag/v0.4 (or search for interrupt-map in the source code for examples)

@avolkov-1221
Copy link
Collaborator

avolkov-1221 commented Aug 27, 2024

This PR seems to be solving mapping from the interrupt line number of the UIC to the interrupt line number of the NVIC. This is typically done using interrupt maps in the devicetree. Has this been considered as an option? It seems quite extreme to rewrite the vector generation table and IRQ macros just to introduce this mapping.

See section 2.4.3 of https://github.com/devicetree-org/devicetree-specification/releases/tag/v0.4 (or search for interrupt-map in the source code for examples)

Not all RA ICUs have m:n mapping, but some of them (RA2 family) have m: to set of subsets of n mapping. I.e. UART0 RX ICU line can be assigned to 0, 4, 8, 12, 16, 20, 24 and 28 NVIC lines, but for UART9 RX only 4 lines are valid: 4, 12, 24 and 28. So using the (modified) script is much easier.

@soburi
Copy link
Member Author

soburi commented Aug 27, 2024

@bjarki-andreasen

This PR seems to be solving mapping from the interrupt line number of the UIC to the interrupt line number of the NVIC. This is typically done using interrupt maps in the devicetree. Has this been considered as an option? It seems quite extreme to rewrite the vector generation table and IRQ macros just to introduce this mapping.

See section 2.4.3 of https://github.com/devicetree-org/devicetree-specification/releases/tag/v0.4 (or search for interrupt-map in the source code for examples)

The Renesas RA ICU allows any function to be assigned to each interrupt, but this is a mechanism for efficiently using a small number of interrupts.

ra_icu

Therefore, if we tried to solve this by mapping, we would be required to redefine the mapping for each case, which would be an extremely painful task for users.
Also, some Renesas RA interrupts have different functions that can be used depending on the grouping conditions, and we think this will be necessary to address such constraints in the future.
(Simply put, the Renesas RA interrupt configuration is very peculiar.
It would be good if there were a method for creating the mapping automatically, but having to configure it manually would detract from the practical usability of this SoC.)

@bjarki-andreasen
Copy link
Collaborator

bjarki-andreasen commented Aug 27, 2024

@soburi

I see, let's use the RTC as an example: We want to connect RTC_ALM, RTC_PLD, and RTC_CUP (38, 39, 40) to NVIC interrupts 0, 1 and 2:

The RTC uses the IRQ number (event) defined by the UIC

&rtc {
        interrupt-parent = &uic;
        interrupts = <38, 39, 40>;
};

The proper way of managing this is an explicit interrupt-map. The UIC node presents an interrupt map which maps interrupts 38, 39 and 40 to an interrupt line of the parent:

&uic {
        interrupt-map = <
                0 38 &nvic 0 0
                0 39 &nvic 0 1
                0 40 &nvic 0 2
        >;
};

this provides complete flexibility in defining shared interrupts, and mapping UIC IRQ numbers to other nexus like DTC and DMAC, for example, routing GPT0_CCMPA (87) to DTC (this is kind of sudo code though since I don't know what the DTC is exactly, I'm just following the UIC block diagram)

&uic {
        interrupt-map = <
                0 38 &nvic 0 0
                0 39 &nvic 0 1
                0 40 &nvic 0 2
                0 87 &dtc  0 0
        >;
};

This solution is already supported in-tree. It requires no changes to the IRQ infrastructure, and is compliant with the devicetree specification.

I don't believe there is any "getting around" the complexity of the interrupt infrastructure. The best option IMO is to model it correctly using the devicetree. Then, on top of the devicetree, a script can be added to automatically generate a naive interrupt-map (mapping enabled peripheral irqs to lowest nvic IRQs) for the &uic which can be inserted at build time, which a user can choose to disable if more granular mapping is required.

The script would generate the following devicetree snippet

&uic {
        interrupt-map = <
                0 38 &nvic 0 0
                0 39 &nvic 0 1
                0 40 &nvic 0 2
                ...
        >;
};

which replaces the need for a custom gen_isr_tables.py, while achieving the same result.

@bjarki-andreasen bjarki-andreasen removed the DNM This PR should not be merged (Do Not Merge) label Aug 27, 2024
@bjarki-andreasen
Copy link
Collaborator

bjarki-andreasen commented Aug 27, 2024

Removed DNM as nack is sufficient while discussing

@avolkov-1221
Copy link
Collaborator

avolkov-1221 commented Aug 27, 2024

This PR seems to be solving mapping from the interrupt line number of the UIC to the interrupt line number of the NVIC. This is typically done using interrupt maps in the devicetree. Has this been considered as an option? It seems quite extreme to rewrite the vector generation table and IRQ macros just to introduce this mapping.
See section 2.4.3 of https://github.com/devicetree-org/devicetree-specification/releases/tag/v0.4 (or search for interrupt-map in the source code for examples)

Not all RA ICUs have m:n mapping, but some of them (RA2 family) have m: to set of subsets of n mapping. I.e. UART0 RX ICU line can be assigned to 0, 4, 8, 12, 16, 20, 24 and 28 NVIC lines, but for UART9 RX only 4 lines are valid: 4, 12, 24 and 28. So using the (modified) script is much easier.

Hi @bjarki-andreasen it looks like you have missed my previous comment. So let me explain my opinion in more detail:

  • I've already tried to play with the interrupts-map, but without success, because
  • it doesn't support very unusual RA2 ICU mapping at all, it's compatible with M:N mapping, when M IRQs from ICU are mapped to N lines of NVIC (it's the case for all RA members except RA2). But RA2 IRQ mapping actually looks like:

image

Groups here are groups of NVIC lines. I.e. user can't just take any available NVIC's interrupt line. No. The user can choose only one free line from predefined groups. So you need an additional argument to specify which lines are allowed and which are not. The situation worsened since the ICU line can be assigned to any free line from the group, and information about occupied lines is unavailable at the driver level. Additionally, lines are shared between multiple peripherals. Also the same type of the peripheral can belongs to the different number of groups. As an example, for some IRQs, the user may choose from 8 predefined lines (PORT_IRQ0 from the above table), but for others, the user has only 4 (PORT_IRQ4 case). And the same PORT driver is used for both PORT0 and PORT4.

So, to summarize, if customer's application is complex enough, and is using almost all NVIC's lines, then make a mistake with wrong IRQ assignment is a piece of cake. Nevertheless if you have an idea on how to implement the above mapping without significant difficulty for customers, you're welcome to share it. I've given up and would prefer to modify the gen_isr_tables.py to automate the search for available lines, and have trivial irq assignments like below for the SCI9 case:

    ...
    interrupts = <ICU_SCI9_TXI>, <ICU_SCI9_RXI>, <ICU_SCI9_TEI>, <ICU_SCI9_ERI>;
    interrupt-names = "txi", "rxi", "tei", "eri";
    ...    

PS Btw using the interrupts-map for the topmost RA members is not a child's play. The RA8m1 has 474:96 lines mapping. Imagine the DTS and welcome to create a map manually ;).

@soburi
Copy link
Member Author

soburi commented Aug 27, 2024

@bjarki-andreasen

This is because the interrupt controller of the Renesas RA has a different abstraction model from the one assumed by the Zephyr, and it is a problem that requires a new design rather than an extension of the existing design.

I see, let's use the RTC as an example: We want to connect RTC_ALM, RTC_PLD, and RTC_CUP (38, 39, 40) to NVIC interrupts 0, 1 and 2:

I got how to use this feature.
But the proposed method seems theoretically possible, but it is enough to force users to get rid of using this SoC due to the complexity and volume issues, as mentioned in #75946 (comment) if it is required to edit by hand.

Then, on top of the devicetree, a script can be added to automatically generate a naive interrupt-map (mapping enabled peripheral irqs to lowest nvic IRQs) for the &uic which can be inserted at build time, which a user can choose to disable if more granular mapping is required.

which replaces the need for a custom gen_isr_tables.py, while achieving the same result.

The problem with this proposal is that the dts fragment cannot be generated until the interrupts that will actually be used are determined, that is until generating zephyr_pre0.elf, making it practically impossible or requiring a fundamental reconstruction of Zephyr's build sequence.

In the development environment provided by Renesas, this is handled by automating it on the IDE side. This SoC implicitly demands such a solution.

I don't believe there is any "getting around" the complexity of the interrupt infrastructure. The best option IMO is to model it correctly using the devicetree.

We need to find a "workaround" to not impose an excessive burden on at least the users of this SoC.

In this PR, the changes are almost all concentrated in an independent Python script,
and there is no impact on external interfaces, other modules, or the build process.
(In reality, it only changes the table's sort order and generates the LUT.)
As I wrote at the beginning, this is a "new design challenge" so I think this is a moderate and effective proposal against it.

@bjarki-andreasen
Copy link
Collaborator

bjarki-andreasen commented Aug 28, 2024

@avolkov-1221 Regarding the map

This is how the interrupt map could be created:

This is the parent domain:

	nvic: interrupt-controller@deadbeef  {
		#address-cells = <0>;
		compatible = "arm,v7m-nvic";
		reg = <0xdeadbeef 0x0000>;
		interrupt-controller;
		#interrupt-cells = <2>;
	};

it is mapped through the ICU, which is the child domain

	icu: interrupt-controller@deadbeef  {
		#address-cells = <0>;
		compatible = "renesas,ra-interrupt-controller-unit";
		reg = <0xdeadbeef 0x0000>;
		interrupt-controller;
		#interrupt-cells = <3>;
	};

Let's create the lookup table.

We have two interrupt cells in the parent domain, in this order:

  • irq
  • flags

We have three interrupt cells in the child domain, in this order:

  • group
  • irq (event)
  • flags
&icu {
        interrupt-map = <
                /* group 0 (all events routed to line 0 of nvic) */
                0 0 0 &nvic 0 0 /* event 0 to nvic line 0 */
                0 1 0 &nvic 0 0 /* event 1 to nvic line 0 */
                0 4 0 &nvic 0 0 /* event 4 to nvic line 0 */
                ...
                /* group 1 (all events routed to line 1 of nvic)  */ 
                1 2 0 &nvic 1 0 /* event 2 to nvic line 1 */
                1 3 0 &nvic 1 0 /* event 3 to nvic line 1 */
                1 4 0 &nvic 0 0 /* event 4 to nvic line 1 */
};

With this interrupt map, the following will route event 1 to nvic line 0

&foo {
        interrupt-parent = &icu;
        interrupts = <0 1 0>;
};

and the following will map event 0 to nvic line 0

&bar {
        interrupt-parent = &icu;
        interrupts = <0 0 0>;
};

which, if you don't support shared interrupts, will produce a build error as both foo and bar are trying to use the same nvic line. It also allows for mapping multiple events to multiple potential nvic lines, like event 4 which can be routed to nvic lines 0 and 1:

&baz {
        interrupt-parent = &icu;
        interrupts = <1 4 0>; /* event 4 routed to nvic line 1 */
};
&baz {
        interrupt-parent = &icu;
        interrupts = <0 4 0>; /* event 4 routed to nvic line 0 */
};

The devicetree is already designed to handle this properly, and you will have to keep the information regarding which line of the icu goes to which lines of the nvic somewhere anyway.

@soburi @avolkov-1221

On a more general note:

The solution in this PR presents a layering violation... The ISR table generation has nothing to do with the UIC... The ISR table is generated for the top level interrupt controller, the NVIC. enabling irq number 0 means enabling the NVICs IRQ number 0. If you need to do some mapping from the UIC domain to the NVIC domain, that is done with a map. If you need to configure the UIC to route its interrupts to the parent NVIC, that is done in SoC/driver level code, using the devicetree.

On top of the layering violation, allowing every vendor to add a completely custom script for generating ISRs will lead to a huge amount of code debt and duplicate code.

@avolkov-1221
Copy link
Collaborator

avolkov-1221 commented Aug 28, 2024

@avolkov-1221 Regarding the map

This is how the interrupt map could be created:

This is the parent domain:

	nvic: interrupt-controller@deadbeef  {
		#address-cells = <0>;
		compatible = "arm,v7m-nvic";
		reg = <0xdeadbeef 0x0000>;
		interrupt-controller;
		#interrupt-cells = <2>;
	};

it is mapped through the ICU, which is the child domain

	icu: interrupt-controller@deadbeef  {
		#address-cells = <0>;
		compatible = "renesas,ra-interrupt-controller-unit";
		reg = <0xdeadbeef 0x0000>;
		interrupt-controller;
		#interrupt-cells = <3>;
	};

Let's create the lookup table.

We have two interrupt cells in the parent domain, in this order:

  • irq
  • flags

We have three interrupt cells in the child domain, in this order:

  • group
  • irq (event)
  • flags
&icu {
        interrupt-map = <
                /* group 0 (all events routed to line 0 of nvic) */
                0 0 0 &nvic 0 0 /* event 0 to nvic line 0 */
                0 1 0 &nvic 0 0 /* event 1 to nvic line 0 */
                0 4 0 &nvic 0 0 /* event 4 to nvic line 0 */
                ...
                /* group 1 (all events routed to line 1 of nvic)  */ 
                1 2 0 &nvic 1 0 /* event 2 to nvic line 1 */
                1 3 0 &nvic 1 0 /* event 3 to nvic line 1 */
                1 4 0 &nvic 0 0 /* event 4 to nvic line 1 */
};

With this interrupt map, the following will route event 1 to nvic line 0

&foo {
        interrupt-parent = &icu;
        interrupts = <0 1 0>;
};

and the following will map event 0 to nvic line 0

&bar {
        interrupt-parent = &icu;
        interrupts = <0 0 0>;
};

which, if you don't support shared interrupts, will produce a build error as both foo and bar are trying to use the same nvic line. It also allows for mapping multiple events to multiple potential nvic lines, like event 4 which can be routed to nvic lines 0 and 1:

&baz {
        interrupt-parent = &icu;
        interrupts = <1 4 0>; /* event 4 routed to nvic line 1 */
};
&baz {
        interrupt-parent = &icu;
        interrupts = <0 4 0>; /* event 4 routed to nvic line 0 */
};

The devicetree is already designed to handle this properly, and you will have to keep the information regarding which line of the icu goes to which lines of the nvic somewhere anyway.

@soburi @avolkov-1221

On a more general note:

The solution in this PR presents a layering violation... The ISR table generation has nothing to do with the UIC... The ISR table is generated for the top level interrupt controller, the NVIC. enabling irq number 0 means enabling the NVICs IRQ number 0. If you need to do some mapping from the UIC domain to the NVIC domain, that is done with a map. If you need to configure the UIC to route its interrupts to the parent NVIC, that is done in SoC/driver level code, using the devicetree.

On top of the layering violation, allowing every vendor to add a completely custom script for generating ISRs will lead to a huge amount of code debt and duplicate code.

@bjarki-andreasen Yes, you are repeating exactly my path with ints maps and masks :). Unfortunately this is wrong one:
ICU is not m:1 or 1:1 controller, it's 1:n controller. Where n can be 4 or 8. In other words one ICU's single line should be mapped to one of any from 4 or 8 free lines from the NVIC's lines group(s).

Let's consider the following example: some application needs 3 SCI modules (SCI0, SCI1 and SCI3), 1 CAN, 1 ADC input, 1 GPT0 CCMPA input and PORT0 pin0 edge interrupt. And it will be using, let's say, only 7 lines from these IPs (the real life situation is more complex): SCI0_RXI, SCI1_RXI, SCI3_RXI, CAN0_ERS, ADC120_ADI, GPT0_CCMPA and PORT_IRQ0. All these peripherals have common NVIC's group0, and some of them also belongs to the group4, so if you try to use your proposed DTS interrupt mapping, the group0 or group4 will overflow, and you'll get error. What's wrong: the second group still have free lines. The interrupts assignment must be more smart: actually groups have different weights defined by the number of all used lines connected to them. AFAIK this information can't be used by the current device tree infrastructure, fix me please, if I wrong. And the assignment must be started from the group with less weight. In this case, the result of the assignment will look like this (the lines within the group are assigned in order from low to high):

  For this case: group4 (w = 5) is less weighted then group0 (w = 7)
  
    SCI0_RXI        -> NVIC g4: irq 4
    SCI1_RXI        -> NVIC g0: irq 0  (since only group0 is available)
    SCI3_RXI        -> NVIC g0: irq 8  (since only group0 is available)
    CAN0_ERS        -> NVIC g4: irq 12 
    ADC120_ADI      -> NVIC g4: irq 20 
    GPT0_CCMPA      -> NVIC g4: irq 28
    PORT_IRQ0       -> NVIC g0: irq 16  (since group4 is full)

For different sets of IPs and for the different peripherals initialization sequences, the mapping will vary, and the complexity of IRQs manually assignment mapping grows non-linearly with the number of peripherals. So, for RA2 family, due to lack of above logic implementation in gen_isr_tables.py, raw IRQ_CONNECT can't be used at all, and irq_connect_dynamic can't be used too, thanks to the lack of VTOR. The result is a notable waste of memory and a significant performance impact.

Regarding additional 3rd parties scripts, we're on the same page. Specifically, I have already shared my point of view here: #77531. However, this case unfortunately seems to be an exception so far. Making changes in global gen_isr_tables.py also doesn’t seem like a suitable solution, as this ICU is quite unique. To be honest, this is the first time I’ve encountered something like this in over 30 years of experience.

@bjarki-andreasen
Copy link
Collaborator

bjarki-andreasen commented Aug 28, 2024

@avolkov-1221 To summarize my point, the is issue in fact not at the ISR table generation level, and the devicetree specification already provides the interrupt-map, which is the way to map interrupts from one domain to another. Yes, it may be inconvenient for the user to have to check the reference manual to figure out the routing, but that is the way to do it. This is common for DMA channels, memory regions, pinctrl maps and other "non user friendly" configs.

Practically, a vendor using this SoC will provide drivers for specific peripherals, and a default layout with some of them enabled, and routed validly. A user who wants to use other peripherals than the default ones, will have to make a conscious decision which peripherals the user can live without. Have you considered what happens if a user tries to enable conflicting peripherals? In this case, the user must look into the reference manual to figure out how to solve it in any case.

If you truly want to help the user, create a SoC level script which looks through the interrupt-map for an SoC, and uses this to generate some visualization of the existing peripherals and possible combinations. You need to store the interrupt-map somewhere anyway (for validation and use in your script), might as well take it directly from the devicetree.

@avolkov-1221
Copy link
Collaborator

@bjarki-andreasen

@avolkov-1221 To summarize my point, the is issue in fact not at the ISR table generation level, and the devicetree specification already provides the interrupt-map, which is the way to map interrupts from one domain to another.
Yes, sure. The purpose of this PR is to add missing functionality needed only by one very specific SOC to the standard gen_isr_tables.py.

Yes, it may be inconvenient for the user to have to check the reference manual to figure out the routing, but that is the way to do it. This is common for DMA channels, memory regions, pinctrl maps and other "non user friendly" configs.

Practically, a vendor using this SoC will provide drivers for specific peripherals, and a default layout with some of them enabled, and routed validly. A user who wants to use other peripherals than the default ones, will have to make a conscious decision which peripherals the user can live without. Have you considered what happens if a user tries to enable conflicting peripherals? In this case, the user must look into the reference manual to figure out how to solve it in any case.

@bjarki-andreasen Sorry, but it's obvious. It's the responsibility of any embedded software engineer. And I disagree with what the vendors have provided. For example, Renesas officially actively ignored Zephyr's existence until last year (btw I've been happily living without Windows for decades, but vendors often think that 'Windows based' is a killer feature of their BSPs). Besides copy and paste evaluation boards hardware are rare in the embedded world of entry/middle-level chips. Also the quality of drivers provided by vendors, well, is not always 'good enough'. I remember a case when Freescale broke my driver from the stable Linux kernel in their BSP. So the developer will create/modify the DTS (and driver) provided by vendor by himself. In the case of this chip, our team have been end users for years. And modification interrupt mappings is a headache (certainly we have some tools for it), and this is despite the fact that our projects are quite close in hardware.

If you truly want to help the user,
First of all I would like to help myself :), and forget about semi-manual mapping.

create a SoC level script which looks through the interrupt-map for an SoC, and uses this to generate some visualization of the existing peripherals and possible combinations.

But this is exactly the scope of this PR. The script (located in soc/renesas) is executed only for one precise task: to create the LUT and generate the static mapping. Ie to fill the functionality gap of device tree infrastructure. Initially, I thought subclassing from the global gen_isr_tables.py was possible, but it was @soburi's decision to write it this way. That's his right.

You need to store the interrupt-map somewhere anyway (for validation and use in your script), might as well take it directly from the devicetree.

Indeed. The group mapping information is encoded in the IRQ numbers. More precisely, in the new header renesas-ra2-icu.h:

...
#define RA2_ICU_EVENT_PORT_IRQ0    RA2_ICU_EV_N(0, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00)
#define RA2_ICU_EVENT_PORT_IRQ1    RA2_ICU_EV_N(0, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00)
#define RA2_ICU_EVENT_PORT_IRQ2    RA2_ICU_EV_N(0, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00)
....
#define RA2_ICU_EVENT_ADC120_ADI   RA2_ICU_EV_N(0, 0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00)
...

And this info is used by my modified script derived from this PR. Besides the device tree for the RA2, based on this numbering scheme become very close to any other device tree:

ra2l1.dtsi:
...
                 interrupt-parent = <&icu>;
                 
                 icu: interrupt-controller@40006000 {
                        compatible = "renesas,ra2-icu";
                        status = "okay";

                        reg = <0x40006000 0x380>;
                        #address-cells = <0>;
                
                        interrupt-controller;
                        #interrupt-cells = <2>;
                };
...
                sci0: sci@40070000 {
                        reg = <0x40070000 0x20>;
                        status = "disabled";
                        lpm-id = <LPM_RA_SCI0>;
                        interrupts = <RA2_ICU_EVENT_SCI0_TXI 3>,
                                              <RA2_ICU_EVENT_SCI0_RXI 3>, 
                                              <RA2_ICU_EVENT_SCI0_TEI 3>, 
                                              <RA2_ICU_EVENT_SCI0_ERI 3>;
                        interrupt-names = "txi", "rxi", "tei", "eri";
                    };
                  

As result the generic RA serial port driver uart_renesas_ra_sci.c (and many others) with IRQ_CONNECT() can also be used almost without any modification. So the developer should check the datasheet only for an IP functionality and which/how interrupts should be used, but not puzzle over how these interrupts should be mapped to NVIC in the internals of Zephyr. However, he can do this at any time, the generated map is naturally available, no one removed the debug output of the gen_isr_tables.py or generated isr_tables.c

@bjarki-andreasen
Copy link
Collaborator

@avolkov-1221

If you truly want to help the user,

First of all I would like to help myself :), and forget about semi-manual mapping.

create a SoC level script which looks through the interrupt-map for an SoC, and uses this to generate some visualization of the existing peripherals and possible combinations.

But this is exactly the scope of this PR. The script (located in soc/renesas) is executed only for one precise task: to create the LUT and generate the static mapping. Ie to fill the functionality gap of device tree infrastructure.

This is where I disagree, there is not a gap of functionality in the device tree infrastructure. The devicetree is fully capable of creating the map, and mapping out the interrupt lines, and IRQ_CONNECT will indeed be able to get the correctly mapped IRQ number.

This PR is adding, not just a new way of defining and configuring interrupts, but opens up the door for all vendors to add their own custom solutions for something that is indeed generic, and well supported by the existing infrastructure. The reason zephyr is useful at all, is that it supports multiple platforms using common infrastructure. The fact that you have to break that infrastructure to provide support for your SoC should be an indication that you may be doing something wrong, it works for every other platform...

@avolkov-1221
Copy link
Collaborator

@bjarki-andreasen

@avolkov-1221

If you truly want to help the user,

First of all I would like to help myself :), and forget about semi-manual mapping.

create a SoC level script which looks through the interrupt-map for an SoC, and uses this to generate some visualization of the existing peripherals and possible combinations.

But this is exactly the scope of this PR. The script (located in soc/renesas) is executed only for one precise task: to create the LUT and generate the static mapping. Ie to fill the functionality gap of device tree infrastructure.

This is where I disagree, there is not a gap of functionality in the device tree infrastructure. The devicetree is fully capable of creating the map, and mapping out the interrupt lines, and IRQ_CONNECT will indeed be able to get the correctly mapped IRQ number.

The current devicetree, as I tried to explain earlier, is not capable of mapping to the pool of interrupts, resolving assignment conflicts, or helping the customer avoid bugs in complex cases. Btw, could you explain in more detail your idea about the 'SoC level script'? Is it a post-/pre-processor for DTS, interrupt-map generator, or something else? Because, sorry for the repetition, the script from this PR is also a 'SoC-level script.'

As a compromise (and probably worst) solution, this PR's script also could be moved to the HAL.

IMHO, a huge manual handled interrupt-map with completely unclear syntax and with tens or hundreds static hardcoded items will not improve the readability or usability of the devicetree. As pointed out earlier in #75946 (comment), due to the complexity of development, customers may start switching to alternative solutions, and not necessarily to another chip, but more likely to another OS.

This PR is adding, not just a new way of defining and configuring interrupts, but opens up the door for all vendors to add their own custom solutions for something that is indeed generic, and well supported by the existing infrastructure. The reason zephyr is useful at all, is that it supports multiple platforms using common infrastructure. The fact that you have to break that infrastructure to provide support for your SoC should be an indication that you may be doing something wrong, it works for every other platform...

Could you also add your opinion as a comment to #77531? Because the current situation with modules is exactly what concerns you, and unfortunately, it has already become a reality. Your statements regarding this PR should ideally be discussed too. That and/or this topic(s) may be discussed at one of the upcoming TCS meetings.

@bjarki-andreasen bjarki-andreasen added the Architecture Review Discussion in the Architecture WG required label Aug 29, 2024
@bjarki-andreasen
Copy link
Collaborator

We definately need to discuss this in a larger forum, added the arch review label.

IMHO, a huge manual handled interrupt-map with completely unclear syntax and with tens or hundreds static hardcoded items will not improve the readability or usability of the devicetree. As pointed out earlier in #75946 (comment), due to the complexity of development, customers may start switching to alternative solutions, and not necessarily to another chip, but more likely to another OS.

I believe you may be overstating the complexity of the interrupt-map, and the amount of work needed for the hardcoded items. The seemingly autogenerated header renesas-ra2-icu.h is also hardcoded, just like the interrupt-map would be. From the perspective of a device which is a child of the UIC, they only information needed is the group, event id and flags, which is gotten from the reference manual, (or by looking at the interrupt-map itself which contains the exact same information).

Btw, could you explain in more detail your idea about the 'SoC level script'? Is it a post-/pre-processor for DTS, interrupt-map generator, or something else? Because, sorry for the repetition, the script from this PR is also a 'SoC-level script.'

It would use the interrupt-map (which is "hardcoded") as an input, just like a user and any SoC level driver for the UIC would, and could help a user identify which specific groups can be used for which peripherals. Validating that the specific interrupts defined by the user are valid would be done by static asserts in the SoC level driver for the UIC.

I'm not sure this script would be of much use, a good README or reference manual explaining the groups and how they map to the peripherals may be more then enough for a user.

@avolkov-1221
Copy link
Collaborator

We definately need to discuss this in a larger forum, added the arch review label.

Nice. Thank you. Sept, 03 or a week later?

IMHO, a huge manual handled interrupt-map with completely unclear syntax and with tens or hundreds static hardcoded items will not improve the readability or usability of the devicetree. As pointed out earlier in #75946 (comment), due to the complexity of development, customers may start switching to alternative solutions, and not necessarily to another chip, but more likely to another OS.

I believe you may be overstating the complexity of the interrupt-map, and the amount of work needed for the hardcoded items. The seemingly autogenerated header renesas-ra2-icu.h is also hardcoded, just like the interrupt-map would be.

The main difference between the map and the header (which is certainly based on information extracted from the datasheet using some scripts) is that the header is generated only once and remains almost constant 'till SOC's EOL, whereas the map is regenerated with each hardware modification and updated manually (welcome the human-factor related bugs). Don't forget also about the map sizes for the topmost members of the RA family (up to 96 items for RA8m1).

From the perspective of a device which is a child of the UIC, they only information needed is the group, event id and flags, which is gotten from the reference manual, (or by looking at the interrupt-map itself which contains the exact same information).

Small correction: an ICU line can belong to multiple NVIC's groups. And the groups info can't be used at the devicetree level. It's merely an additional condition for selecting from the pool of target IRQs. If assignment from the IRQ pool is not supported, then the group is useless either. Ah, and flags are not supported by IRQ_CONNECT, issue #75851

Btw, could you explain in more detail your idea about the 'SoC level script'? Is it a post-/pre-processor for DTS, interrupt-map generator, or something else? Because, sorry for the repetition, the script from this PR is also a 'SoC-level script.'

It would use the interrupt-map (which is "hardcoded") as an input, just like a user and any SoC level driver for the UIC would, and could help a user identify which specific groups can be used for which peripherals. Validating that the specific interrupts defined by the user are valid would be done by static asserts in the SoC level driver for the UIC.

I'm not sure this script would be of much use, a good README or reference manual explaining the groups and how they map to the peripherals may be more then enough for a user.

I've just figured that some devicetree intermediate postprocessor script, which generates a map based on the interrupts used in the DTS, could be a reasonable compromise.

@bjarki-andreasen
Copy link
Collaborator

bjarki-andreasen commented Aug 29, 2024

Nice. Thank you. Sept, 03 or a week later?

Should be Sept, 03

The main difference between the map and the header (which is certainly based on information extracted from the datasheet using some scripts) is that the header is generated only once and remains almost constant 'till SOC's EOL, whereas the map is regenerated with each hardware modification and updated manually (welcome the human-factor related bugs).

I don't believe this is the case, the interrupt-map contains all, and only possible mappings, it remains constant with the SoC, the only thing updated by the user is which entries in the interrupt-map are used, the interrupt-map itself is never changed. This is an example snippet from the dts spec:

interrupt-map = <
        /* IDSEL 0x11 - PCI slot 1 */
        0x8800 0 0 1 &open-pic 2 1 /* INTA */
        0x8800 0 0 2 &open-pic 3 1 /* INTB */
        0x8800 0 0 3 &open-pic 4 1 /* INTC */
        0x8800 0 0 4 &open-pic 1 1 /* INTD */
        /* IDSEL 0x12 - PCI slot 2 */
        0x9000 0 0 1 &open-pic 3 1 /* INTA */
        0x9000 0 0 2 &open-pic 4 1 /* INTB */
        0x9000 0 0 3 &open-pic 1 1 /* INTC */
        0x9000 0 0 4 &open-pic 2 1 /* INTD */
>;

This map remains constant for the lifetime of the hardware. INTA can be connected to IRQ 1 of PCI slot 1, and IRQ 1 of PCI slot 2, these are the possible physical connections.

Modifying the sample to be a bit closer to the UIC (the first child interrupt specifier is group, second is line):

interrupt-map = <
        /* NVIC IRQ 0 (group 0 right?) */
        0 0  &nvic 0 0 /* ICU Line 0 */
        0 4  &nvic 0 0 /* ICU Line 4 (notice can be routed to NVIC IRQ 0 and 1) */
        0 8  &nvic 0 0 /* ICU Line 8 */
        0 12 &nvic 0 0 /* ICU Line 12 */
        /* NVIC IRQ 1 (group 1 right?) */
        1 1  &nvic 1 0 /* ICU Line 1 */
        1 2  &nvic 1 0 /* ICU Line 2 */
        1 3  &nvic 1 0 /* ICU Line 3 */
        1 4  &nvic 1 0 /* ICU Line 4 (notice can be routed to NVIC IRQ 0 and 1) */
>;

All of the information is constant, and only possible combinations of group/line can be selected from the map. Unless shared interrupts are enabled, attempting to route two ICU lines to the same NVIC IRQ will produce a build error, and can be detected by the UIC SoC level driver as well.

The only change made by a user, if required, is switching which mapping is used:

&foo {
        interrupt-parent = <&uic>;
        interrupts = <0 4>; /* connected to NVIC IRQ 0 */
};

or

&foo {
        interrupt-parent = <&uic>;
        interrupts = <1 4>; /* connected to NVIC IRQ 1 */
};

trying to route UIC line 0 to NVIC IRQ 1 would fail:

&bar {
        interrupt-parent = <&uic>;
        interrupts = <1 0>; /* invalid map since there is no 1 0 map entry */
};

@bjarki-andreasen
Copy link
Collaborator

bjarki-andreasen commented Aug 29, 2024

I have done my best to make the devicetree snippet match the table you showed earlier here:

The specifiers in the map are:

interrupt-map = <
        <EILSRn> <Register setting> <UIC flags> <parent domain> <irq line> <irq flags>
>;
interrupt-map = <
        /* IELSR0 (group 0) (nvic line 0) */
        0 1 0  &nvic 0 0 /* PORT_IRQ0 */
        0 2 0  &nvic 0 0 /* DTC_COMPLETE */
        0 3 0  &nvic 0 0 /* ICU_SNZCANCEL */
        0 4 0  &nvic 0 0 /* LVD_LVD1 */
        /* IELSR1 (group 1) (nvic line 1) */
        1 1 0  &nvic 1 0 /* PORT_IRQ1 */
        1 2 0  &nvic 1 0 /* LVD_LVD2 */
        /* IELSR2 (group 2) (nvic line 2) */
        2 1 0  &nvic 2 0 /* PORT_IRQ2 */
        2 2 0  &nvic 2 0 /* FCU_FRDYI */
        ...
        /* IELSR4 (group 4) (nvic line 4) */
        4 1 0  &nvic 4 0 /* PORT_IRQ0 */
        ...
        /* IELSR8 (group 0) (nvic line 8) */
        8 1 0  &nvic 8 0 /* PORT_IRQ0 */
        8 2 0  &nvic 8 0 /* DTC_COMPLETE */
        8 3 0  &nvic 8 0 /* ICU_SNZCANCEL */
        8 4 0  &nvic 8 0 /* LVD_LVD1 */
>;

With this, we can only apply valid mappings according to the groups, let's connect event PORT_IRQ0 to NVIC IRQ line 4, and DTC_COMPLETE to NVIC 0. Note here that PORT_IRQ0 can be connected to NVIC IRQ 0 (group 0), 4 (group 4), 8 (group 0), 16 (group 0) and 24 (group 0). We choose to use NVIC IRQ 4 since we also want to use DTC_COMPLETE.

&port {
        interrupts = <4 1 IRQ_RISING_EDGE>; /* Route to NVIC IRQ 4 and pass flag IRQ_RISING_EDGE */
};

&dtc {
        interrupts = <0 2 IRQ_RISING_EDGE>; /* Route to NVIC IRQ 0 and pass flag IRQ_RISING_EDGE */
};

@avolkov-1221
Copy link
Collaborator

avolkov-1221 commented Aug 29, 2024

I don't believe this is the case, the interrupt-map contains all, and only possible mappings, it remains constant with the SoC, the only thing updated by the user is which entries in the interrupt-map are used, the interrupt-map itself is never changed. This is an example snippet from the dts spec:

interrupt-map = <
        /* IDSEL 0x11 - PCI slot 1 */
        0x8800 0 0 1 &open-pic 2 1 /* INTA */
        0x8800 0 0 2 &open-pic 3 1 /* INTB */
        0x8800 0 0 3 &open-pic 4 1 /* INTC */
        0x8800 0 0 4 &open-pic 1 1 /* INTD */
        /* IDSEL 0x12 - PCI slot 2 */
        0x9000 0 0 1 &open-pic 3 1 /* INTA */
        0x9000 0 0 2 &open-pic 4 1 /* INTB */
        0x9000 0 0 3 &open-pic 1 1 /* INTC */
        0x9000 0 0 4 &open-pic 2 1 /* INTD */
>;

This map remains constant for the lifetime of the hardware. INTA can be connected to IRQ 1 of PCI slot 1, and IRQ 1 of PCI slot 2, these are the possible physical connections.

Modifying the sample to be a bit closer to the UIC (the first child interrupt specifier is group, second is line):

interrupt-map = <
        /* NVIC IRQ 0 (group 0 right?) */
        0 0  &nvic 0 0 /* ICU Line 0 */
        0 4  &nvic 0 0 /* ICU Line 4 (notice can be routed to NVIC IRQ 0 and 1) */
        0 8  &nvic 0 0 /* ICU Line 8 */
        0 12 &nvic 0 0 /* ICU Line 12 */
        /* NVIC IRQ 1 (group 1 right?) */
        1 1  &nvic 1 0 /* ICU Line 1 */
        1 2  &nvic 1 0 /* ICU Line 2 */
        1 3  &nvic 1 0 /* ICU Line 3 */
        1 4  &nvic 1 0 /* ICU Line 4 (notice can be routed to NVIC IRQ 0 and 1) */
>;

You are wrong here: you continue to treat the ICU as an n:1 controller, but it's actually a 1:n one. To differentiate the source lines, a bitmask of valid groups should be added as a part of source line ID. Then, the map of the first 14 lines would look like this

interrupt-map = <
  0x1001 0x01 &nvic  0 /* IELS0  = 1,    NVIC lines can be any of 0, 4, 8, 12, 16, 20, 24, 28, line belongs to g0 and g4  */
  0x2002 0x01 &nvic  1 /* IELS1  = 1,    NVIC lines can be any of 1, 5, 9, 13, 17, 21, 25, 29. Groups g1 and g5 */
  0x4004 0x01 &nvic  2 /* IELS2  = 1,    NVIC lines can be any of 2, 6, 10, 14, 18, 22, 26, 30. Groups g2 and g6 */
  0x0010 0x16 &nvic  4 /* IELS4  = 0x16, NVIC lines can be any of 4, 12, 20, 28. Only group g4 */
  0x0020 0x13 &nvic  5 /* IELS5  = 0x13, NVIC lines can be any of 5, 13, 21, 29. Only group g5 */
  0x0040 0x13 &nvic  6 /* IELS6  = 0x13, NVIC lines can be any of 6, 14, 22, 30. Only group g6 */
  0x0080 0x11 &nvic  7 /* IELS7  = 0x11, NVIC lines can be any of 7, 15, 23, 31. Only group g7 */
  0x1001 0x02 &nvic  8 /* IELS8  = 0x2,  NVIC lines can be any of 0, 4, 8, 12, 16, 20, 24, 28. Groups g0 and g4 */
  0x1001 0x03 &nvic 12 /* IELS12 = 0x3,  NVIC lines can be any of 0, 4, 8, 12, 16, 20, 24, 28. Groups g0 and g4 */
  0x4004 0x02 &nvic 14 /* IELS14 = 0x2,  NVIC lines can be any of 2, 6, 10, 14, 18, 22, 26, 30. Groups g2 and g6 */
  0x1001 0x04 &nvic 16 /* IELS16 = 0x4,  NVIC lines can be any of 0, 4, 8, 12, 16, 20, 24, 28. Groups g0 and g4 */
  0x2002 0x02 &nvic  9 /* IELS9  = 0x2,  NVIC lines can be any of 1, 5, 9, 13, 17, 21, 25, 29. Groups g1 and g5 */
  0x0040 0x14 &nvic 22 /* IELS22 = 0x14, NVIC lines can be any of 6, 14, 22, 30. Only group g6 */
  0x8008 0x02 &nvic  3 /* IELS3  = 0x2,  NVIC lines can be any of 3, 7, 11, 15, 19, 23, 27, 31. Groups g3 and g7 */
  ....
;

The problem here is that there are 154 ICU lines, but only 32 grouped valid NVICs, so adding or replacing any peripheral makes the map invalid, since each ICU interrupt line has predefined groups combination, and removing one IP doesn't mean that another can be used instead (even of the same type). Some hardware combinations make the mapping impossible. Updating it correctly manually without a special tool (like the Renesas-provided e2 with FSP) is not so easy task. Therefore, if we add a mapping script, the above mapping can be generated and verified automatically in time of DTS postprocessing, or, easier, in linking time.

NB: this nightmare exists only for RA2 series, all other (RA4, RA6 and RA8) have more adequate ICU, where any ICU line can be assigned to any free NVIC one. Limitation only in relation of ICU to NVIC lines numbers (474:96 accordingly in one of the topmost RA8m1). I.e. for them, the script should perform the mapping until an NVIC overflow occurs. But changing the peripheral set will invalidate the map in any case: you'll need to manage the free lines counter somehow, and you can't use a static NVIC mapping, as the NVIC line numbers depend solely on the number of chosen IPs. New IPs combination == new NVIC table.

@avolkov-1221
Copy link
Collaborator

I have done my best to make the devicetree snippet match the table you showed earlier here:

The specifiers in the map are:

interrupt-map = <
        <EILSRn> <Register setting> <UIC flags> <parent domain> <irq line> <irq flags>
>;
interrupt-map = <
        /* IELSR0 (group 0) (nvic line 0) */
        0 1 0  &nvic 0 0 /* PORT_IRQ0 */
        0 2 0  &nvic 0 0 /* DTC_COMPLETE */
        0 3 0  &nvic 0 0 /* ICU_SNZCANCEL */
        0 4 0  &nvic 0 0 /* LVD_LVD1 */
        /* IELSR1 (group 1) (nvic line 1) */
        1 1 0  &nvic 1 0 /* PORT_IRQ1 */
        1 2 0  &nvic 1 0 /* LVD_LVD2 */
        /* IELSR2 (group 2) (nvic line 2) */
        2 1 0  &nvic 2 0 /* PORT_IRQ2 */
        2 2 0  &nvic 2 0 /* FCU_FRDYI */
        ...
        /* IELSR4 (group 4) (nvic line 4) */
        4 1 0  &nvic 4 0 /* PORT_IRQ0 */
        ...
        /* IELSR8 (group 0) (nvic line 8) */
        8 1 0  &nvic 8 0 /* PORT_IRQ0 */
        8 2 0  &nvic 8 0 /* DTC_COMPLETE */
        8 3 0  &nvic 8 0 /* ICU_SNZCANCEL */
        8 4 0  &nvic 8 0 /* LVD_LVD1 */
>;

With this, we can only apply valid mappings according to the groups, let's connect event PORT_IRQ0 to NVIC IRQ line 4, and DTC_COMPLETE to NVIC 0. Note here that PORT_IRQ0 can be connected to NVIC IRQ 0 (group 0), 4 (group 4), 8 (group 0), 16 (group 0) and 24 (group 0). We choose to use NVIC IRQ 4 since we also want to use DTC_COMPLETE.

&port {
        interrupts = <4 1 IRQ_RISING_EDGE>; /* Route to NVIC IRQ 4 and pass flag IRQ_RISING_EDGE */
};

&dtc {
        interrupts = <0 2 IRQ_RISING_EDGE>; /* Route to NVIC IRQ 0 and pass flag IRQ_RISING_EDGE */
};

IMO the situation here is worse than before: you have a static map, but variable IP's nodes probably spreaded over many .overlay and .dtsi files. And the problem is the same: NVIC's lines are constant. What's not true, the ICU lines can be static, not NVIC's ones (check my previous example for the first 14 lines).

@bjarki-andreasen
Copy link
Collaborator

bjarki-andreasen commented Aug 30, 2024

I have done my best to make the devicetree snippet match the table you showed earlier here:
The specifiers in the map are:

interrupt-map = <
        <EILSRn> <Register setting> <UIC flags> <parent domain> <irq line> <irq flags>
>;
interrupt-map = <
        /* IELSR0 (group 0) (nvic line 0) */
        0 1 0  &nvic 0 0 /* PORT_IRQ0 */
        0 2 0  &nvic 0 0 /* DTC_COMPLETE */
        0 3 0  &nvic 0 0 /* ICU_SNZCANCEL */
        0 4 0  &nvic 0 0 /* LVD_LVD1 */
        /* IELSR1 (group 1) (nvic line 1) */
        1 1 0  &nvic 1 0 /* PORT_IRQ1 */
        1 2 0  &nvic 1 0 /* LVD_LVD2 */
        /* IELSR2 (group 2) (nvic line 2) */
        2 1 0  &nvic 2 0 /* PORT_IRQ2 */
        2 2 0  &nvic 2 0 /* FCU_FRDYI */
        ...
        /* IELSR4 (group 4) (nvic line 4) */
        4 1 0  &nvic 4 0 /* PORT_IRQ0 */
        ...
        /* IELSR8 (group 0) (nvic line 8) */
        8 1 0  &nvic 8 0 /* PORT_IRQ0 */
        8 2 0  &nvic 8 0 /* DTC_COMPLETE */
        8 3 0  &nvic 8 0 /* ICU_SNZCANCEL */
        8 4 0  &nvic 8 0 /* LVD_LVD1 */
>;

With this, we can only apply valid mappings according to the groups, let's connect event PORT_IRQ0 to NVIC IRQ line 4, and DTC_COMPLETE to NVIC 0. Note here that PORT_IRQ0 can be connected to NVIC IRQ 0 (group 0), 4 (group 4), 8 (group 0), 16 (group 0) and 24 (group 0). We choose to use NVIC IRQ 4 since we also want to use DTC_COMPLETE.

&port {
        interrupts = <4 1 IRQ_RISING_EDGE>; /* Route to NVIC IRQ 4 and pass flag IRQ_RISING_EDGE */
};

&dtc {
        interrupts = <0 2 IRQ_RISING_EDGE>; /* Route to NVIC IRQ 0 and pass flag IRQ_RISING_EDGE */
};

IMO the situation here is worse than before: you have a static map, but variable IP's nodes probably spreaded over many .overlay and .dtsi files. And the problem is the same: NVIC's lines are constant. What's not true, the ICU lines can be static, not NVIC's ones (check my previous example for the first 14 lines).

you have a static map, but variable IP's nodes

Exactly, that's an accurate model of this SoCs hardware.

As I mentioned before, this is the case for pinctrl, DMAs, memory regions, etc. These are also modified by the "user" to match their usecases, and have even more strange constraints like which memory region can a specific DMA read from, and which specific peripheral can use which specific DMA channels.

Can we at least agree that the above interrupt-map is a viable solution to correctly routing any peripherals event to any valid NVIC channel?

@avolkov-1221
Copy link
Collaborator

avolkov-1221 commented Aug 30, 2024

As I mentioned before, this is the case for pinctrl, DMAs, memory regions, etc. These are also modified by the "user" to match their usecases, and have even more strange constraints like which memory region can a specific DMA read from, and which specific peripheral can use which specific DMA channels.

Can we at least agree that the above interrupt-map is a viable solution to correctly routing any peripherals event to any valid NVIC channel?

Yes sure, and I didn't really claimed otherwise :). I have stated and continue to state that:

IMHO, a huge manual handled interrupt-map with completely unclear syntax and with tens or hundreds static hardcoded items will not improve the readability or usability of the devicetree. As pointed out earlier in #75946 (comment), due to the complexity of development, customers may start switching to alternative solutions, and not necessarily to another chip, but more likely to another OS.

Sorry, but updating pinctrl and DMA maps doesn't require rewriting most of the items; usually, only those related to the changed hardware are enough, and it's not as painful as dealing with RA2 IRQs. So using them as an example here is not quite appropriate. Thus, the question about of some auxiliary script remains unresolved. Rewriting a significant part of the very error-prone map, with every small change of hardware is not something that will attract customers. Especially if we want to have "zephyr is useful at all" :).

Btw it seems that the script could be more universal and not so RA-specific, making it usable for other interrupt controllers where 'IRQ pool' assignment is required. Besides I don't have a better term for this type of assignment, and I'm not sure that it's correct. If anyone has one, please correct me.

@bjarki-andreasen
Copy link
Collaborator

Sorry, but updating pinctrl and DMA maps doesn't require rewriting most of the items; usually, only those related to the changed hardware are enough, and it's not as painful as dealing with RA2 IRQs. So using them as an example here is not quite appropriate.

I simply disagree with this statement.

This is where a DMA channel is selected and configured, in a single location, on a single device node:

&usart3 {
	dmas = <&gpdma1 0 29 STM32_DMA_PERIPH_TX
		&gpdma1 1 28 STM32_DMA_PERIPH_RX>;
	dma-names = "tx", "rx";
};

This is where a free NVIC line is selected and configured, in a single location, on a single device node:

&usart3 {
        interrupts = <0 1 0>;
};

This is where free pins are selected and configured, in a single location, on a single device node:

&usart3 {
	pinctrl-0 = <&usart3_tx_pd8 &usart3_rx_pd9>;
	pinctrl-names = "default";
};

True for all of them is that selecting a specific DMA/IRQ/PINs must be done with consideration to the rest of the devices in the system, and validity of the configuration (can a specific pin be routed to RX of USART3 at all). The interrupt-map actually makes it easier to select valid configurations than DMA channels and PINs which have no constraints on them at all in the devicetree.

@avolkov-1221
Copy link
Collaborator

avolkov-1221 commented Aug 31, 2024

Sorry, but updating pinctrl and DMA maps doesn't require rewriting most of the items; usually, only those related to the changed hardware are enough, and it's not as painful as dealing with RA2 IRQs. So using them as an example here is not quite appropriate.

I simply disagree with this statement.

This is where a DMA channel is selected and configured, in a single location, on a single device node:

&usart3 {
	dmas = <&gpdma1 0 29 STM32_DMA_PERIPH_TX
		&gpdma1 1 28 STM32_DMA_PERIPH_RX>;
	dma-names = "tx", "rx";
};

This is where a free NVIC line is selected and configured, in a single location, on a single device node:

&usart3 {
        interrupts = <0 1 0>;
};

This is where free pins are selected and configured, in a single location, on a single device node:

&usart3 {
	pinctrl-0 = <&usart3_tx_pd8 &usart3_rx_pd9>;
	pinctrl-names = "default";
};

True for all of them is that selecting a specific DMA/IRQ/PINs must be done with consideration to the rest of the devices in the system, and validity of the configuration (can a specific pin be routed to RX of USART3 at all). The interrupt-map actually makes it easier to select valid configurations than DMA channels and PINs which have no constraints on them at all in the devicetree.

@bjarki-andreasen Well, I've tried to implement the interrupt-map, but without any success. It seems like there's a bug in the edtlib, specifically in the _map function. The function tries to use the #address-cells from the device's node where the interrupts property is located, instead of the icu one. As a result, the mapping is unpredictable. Or I've made mistake with device tree syntax. Could you check the gen_defines.py functionality? I'm not sure about the result: is it my mistake or new issue.

@bjarki-andreasen
Copy link
Collaborator

@bjarki-andreasen Well, I've tried to implement the interrupt-map, but without any success. It seems like there's a bug in the edtlib, specifically in the _map function. The function tries to use the #address-cells from the device's node where the interrupts property is located, instead of the icu one. As a result, the mapping is unpredictable. Or I've made mistake with device tree syntax. Could you check the gen_defines.py functionality? I'm not sure about the result: is it my mistake or new issue.

If we agree to use the devicetree interrupt-map, I will definitely help ensure edtlib functions as intended :) I'm learning as we discuss here as well, it will be nice to try and actually implement it :)

@avolkov-1221
Copy link
Collaborator

@bjarki-andreasen Well, I've tried to implement the interrupt-map, but without any success. It seems like there's a bug in the edtlib, specifically in the _map function. The function tries to use the #address-cells from the device's node where the interrupts property is located, instead of the icu one. As a result, the mapping is unpredictable. Or I've made mistake with device tree syntax. Could you check the gen_defines.py functionality? I'm not sure about the result: is it my mistake or new issue.

If we agree to use the devicetree interrupt-map, I will definitely help ensure edtlib functions as intended :) I'm learning as we discuss here as well, it will be nice to try and actually implement it :)

I've created an issue: #77890. To simplify the discussion.

@carlescufi
Copy link
Member

carlescufi commented Sep 3, 2024

Architecture WG:

  • @avolkov-1221 describes the issue, where there is an M:N mapping between ICU interrupts and the NVIC. This can be in the order of 400:60, which complicates things.
  • @bjarki-andreasen states that the current solution, which is to fork gen_isr_tables.py and modify it, is not appropriate. This script has nothing to do with mapping interrupts between controllers, but rather generating the tables themselves. Bjarki shows how a particular device can be mapped to a particular NVIC interrupt line.
  • @bjarki-andreasen suggests using an interrupt-map in Devicetree to describe the allowed combinations, and then assign interrupts directly from the peripheral nodes.
  • @avolkov-1221 suggests running a script during the build process to check the content of the interrupt-map and verify that all interrupts comply with the map
  • @avolkov-1221 to draft a proposal

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Architecture Review Discussion in the Architecture WG required area: Architectures area: ARM ARM (32-bit) Architecture area: Build System area: Devicetree Binding PR modifies or adds a Device Tree binding area: Devicetree area: GPIO area: Interrupt Controller area: UART Universal Asynchronous Receiver-Transmitter platform: Renesas RA Renesas Electronics Corporation, RA platforms: Renesas RA
Projects
Status: Todo
Development

Successfully merging this pull request may close these issues.