Menu Close

RISC-V PLIC CPU Design

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.

IMG_257

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.

IMG_258

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.

IMG_259

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.

IMG_260

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].

Posted in FPGA, RISC-V, RISC-V Textbook, Textbook and Training Project

Related Articles

Leave a Reply

Your email address will not be published.

Leave the field below empty!