1. Interrupt source (ID) definition
Related reference articles:
RISC-V teaching plan
The definition of the interrupt source on the address map (click here for details ) is shown in Figure 1. It can be seen that the current address map V1.2 defines 63 interrupt sources (interrupt source 0 is no interrupt).
Figure 1 Interrupt Source (ID)
The implementation of the interrupt source inside the CPU is as follows, in which the specific external interrupt requests are connected to the respective module outputs. For example pwm3_cmp_irq (interrupt request for pwm3 comparison result) is the output in the PWM module. The i_plic_irq, which brings together all types of interrupt sources, will be input to the external platform-level interrupt processing (PLIC) core for arbitration to elect the interrupt request to be processed. That is, when multiple external interrupt requests are generated at the same time, the PLIC core will only allow one interrupt to occur.
wire i2c3_irq; wire i2c2_irq; wire i2c1_irq; wire i2c0_irq; wire [3:0] pwm3_cmp_irq; wire [3:0] pwm2_cmp_irq; wire [3:0] pwm1_cmp_irq; wire[3:0] pwm0_cmp_irq ; wire spi3_irq; wire spi2_irq; wire spi1_irq; wire spi0_irq; wire[31:0] o_GPIO_irq; wire uart3_irq; wire uart2_irq; wire uart1_irq; wire uart0_irq; wire rtc_cmp1_irq; wire rtc_cmp0_irq; wire watchdog_irq; //Note that the external interrupt sources here are arranged from high to low wire [PLIC_IRQ_NUM - 1:0] i_plic_irq = { i2c3_irq, i2c2_irq, i2c1_irq, i2c0_irq, pwm3_cmp_irq[3:0], //56-59 pwm2_cmp_irq[3:0], //52-55 pwm1_cmp_irq[3:0], //48-51 pwm0_cmp_irq[3:0], //44-47 spi3_irq, spi2_irq, spi1_irq, spi0_irq, o_GPIO_irq[31:0], // 8-39 uart3_irq, uart2_irq, uart1_irq, uart0_irq, rtc_cmp1_irq, rtc_cmp0_irq, watchdog_irq, 1'b0 //Note that interrupt source 0 means no interrupt };
2. Interrupt source gateway (gateway) definition
Each interrupt source has a gate, which mainly handles interrupt signals and sends them to the PLIC core, which latches these interrupt signals in the interrupt pending bits. Each interrupt source has a separate priority. When the target interrupt source enable is pulled high and the priority is greater than the threshold, the PLIC core will send an interrupt notification to the target. After the target receives the external interrupt, it sends an interrupt declaration to the kernel, requesting to retrieve the pending interrupt with the highest priority, and clear the corresponding interrupt pending bit. After the interrupt completes, the target sends a completion message to the gate.
Only one interrupt request can be suspended in the PLIC core per interrupt source. The gate forwards new interrupt requests to the PLIC core only after receiving notification from the same-origin previous interrupt handler that it has completed.
Figure 2 shows the flow of PLIC to interrupt processing. It can be seen that the gateway forwards only one interrupt request to the PLIC at a time, and does not forward subsequent interrupt requests until the completion of the interrupt is received. It may take a while for the target to respond to the arrival of a new interrupt, but then an interrupt request will be sent to the PLIC core to obtain the interrupt ID. The PLIC core will automatically return the ID and clear the corresponding IP bit, after which no other target can assert the same interrupt request. After the handler finishes handling the interrupt, it sends an interrupt complete message to the gateway to allow new interrupt requests.
Figure 2 PLIC interrupt processing flow [1]
The corresponding PLIC gateway codes (only relevant parts are shown) are as follows:
// Implement gates for each interrupt source IRQ_gateway IRQ_gateway_inst ( .clk ( clk ), //clock .rst_n ( rst_n ), //reset .i_interrupt ( plic_irq_i_r ), //interrupt source .o_plic_valid ( irq_i_gated_valid ),//Whether the output interrupt is valid through the gate .i_plic_read ( irq_i_gated_ready ),//cpu read .i_plic_write ( rib_complete_irq ) //cpu write ); //Define the gate handshake signal, which is determined by the interrupt valid through the gate signal & CPU read signal assign irq_i_gated_hsked = irq_i_gated_valid & irq_i_gated_ready; //gateway module module IRQ_gateway ( input clk, //clock input i_interrupt, //interrupt source output o_plic_valid, //Whether the output interrupt is valid through the gate input i_plic_read, //cpu read input i_plic_write, //cpu write input rst_n //reset ); reg inFlight; //If inFlight is 0 and an interrupt source arrives, the output interrupt is valid, otherwise the output interrupt is invalid assign o_plic_valid = (inFlight == 1'h0) ? i_interrupt : 1'b0; wire int_valid = i_interrupt & i_plic_read; //cpu read & interrupt source arrives always @(posedge clk or negedge rst_n) if (!rst_n) begin inFlight <= 1'h0; end else begin if (i_plic_write) //If cpu writes, inFlight is 0 inFlight <= 1'h0; else if (int_valid) //cpu read & interrupt source arrives, inFlight is 1 inFlight <= 1'h1; end endmodule
3. Interruption of Arbitration
Part of the code for comparing and selecting dangling interrupts is shown in Figure 3. There are a total of 10 levels of comparison arrays. The logic of its implementation is to compare the adjacent numbers of each layer of arrays, give the larger number, and put it into the next layer of arrays until the largest number is obtained. Because sifive company defines 1024 interrupt sources, in order to be compatible with this, the module used by FII RISC-V CPU for interrupt arbitration can also accommodate a maximum of 1024 interrupt sources (actually 1023, because interrupt source 0 indicates that no interrupt occurs), Then, after each level of array comparison, the next level of array is doubled. Because log(2)1024 = 10, a 10-level comparison array is used here to select the interrupt source with the highest priority.
Figure 3 Arbitration code
Figure 4 is an example of a 3-level comparison of arrays, where the numbers in the squares refer to the index of the array . The use of array indexing in the code in Figure 3 can be understood by looking at Figure 4.
Figure 4 Comparison structure
Figure 5 shows an example of randomly placing numbers 1-8 into array indices 0-7. It can be seen that after each layer of comparison, the final remaining number is the largest number among 1-8, 8.
Figure 5 Arbitration example
4. Interrupt suspension (part)
The relevant code is as follows:
// Implement interrupt pending bits for each interrupt source // If the pending interrupt source is cleared, new interrupts from the gate can be accepted assign irq_i_gated_ready = (~irq_pend_r); // When the gateway outputs the handshake signal, the interrupt source hangs up assign irq_pend_set = irq_i_gated_hsked; //After declaring the highest priority suspension interrupt, the corresponding interrupt suspension is cleared assign irq_pend_clr = rib_claim_irq; // If an interrupt is asserted, clear the corresponding interrupt source pending assign irq_pend_ena = (irq_pend_set | irq_pend_clr); //Enable to set up signal by interrupt suspension or hang clear signal assign irq_pend_nxt = (irq_pend_set | (~irq_pend_clr)); //Input the hanging signal of d flip flop fii_dfflr #(1) irq_pend_dfflr(irq_pend_ena , irq_pend_nxt, irq_pend_r, clk, rst_n); //Latch
5. Article references
[1] Www2.eecs.berkeley.edu , 2021. [Online]. Available: https://www2.eecs.berkeley.edu/Pubs/TechRpts/2016/EECS-2016-129.pdf. [Accessed: 29- Mar-2021].