Lab 12. Priority Interrupt Unit
In the basic version of the lab work, it is proposed to implement a processor system with a single interrupt source, which is sufficient for completing the labs. However, if you wish to improve the system and increase the number of peripheral devices, supporting only one interrupt source will create many complications. In this lab work, you need to implement a priority interrupt unit and integrate it into the interrupt controller, increasing the number of potential interrupt sources to 16.
Goal
- Develop a priority interrupt unit (PIU) built using a daisy chain scheme.
- Integrate the PIU into the interrupt controller.
Theory
If the processor system is expected to have more than one interrupt source, it is necessary to determine how to handle collisions — simultaneous interrupt requests from multiple sources. One way to solve this problem is to implement interrupt priorities. From a circuit design perspective, the simplest approach is to implement a scheme with a static, fixed priority. One such scheme is a daisy chain. An example of such a scheme can be seen in Fig. 1.
Figure 1. Daisy chain block diagram.
This scheme consists of two arrays of AND gates. The first array (the top row of elements) generates a multi-bit signal (let's call it ready, shown in Fig. 1 as "Priority"), which is ANDed with the interrupt requests using the bottom row of AND gates, producing the multi-bit signal y. Note that the result of the AND operation on each element of the bottom array affects the AND result of the next element in the top array, and vice versa (readyₙ₊₁ depends on yₙ, while yₙ depends on readyₙ). As soon as any bit of y becomes 1, it immediately propagates as 0 through all subsequent bits of ready, zeroing them. Once zeroed, the ready bits zero the corresponding bits of y (zero bits in ready prevent interrupt generation for the corresponding bits of y).
The bottom array of AND gates can be described using a continuous assignment of the bitwise AND between ready and the interrupt request signal.
To describe the top row of AND gates, you will need to make a continuous assignment of readyₙ & !yₙ to the n+1-th bit of ready. The generate for construct is convenient for this, as it allows automating the creation of many identical structures.
Let's examine how this construct works. Suppose we want to create a bitwise assignment of a 5-bit signal a from a 5-bit signal b.
Indices used by the construct must be declared with the genvar keyword. Then, within the area bounded by the generate/endgenerate keywords, a loop of assignments is described (modules can also be instantiated inside such a loop):
logic [4:0] a;
logic [4:0] b;
// ...
genvar i;
generate
for(i = 0; i < 5; i++) begin
assign a[i] = b[i];
end
endgenerate
Listing 1. Example of using the generate construct.
Of course, in this example one could simply write a single continuous assignment assign a = b;, but for implementing the top row of AND gates, such a multi-bit continuous assignment would not synthesize the required circuit.
Practice
Let's consider the interrupt controller implementation shown in Fig. 2.
Figure 2. Block diagram of the priority interrupt unit.
In addition to ports clk_i and rst_i, the daisy_chain module will have 3 inputs and 3 outputs:
masked_irq_i— 16-bit input for a masked interrupt request (i.e., the interrupt source has already been masked by themiecontrol and status register signal).irq_ret_i— signal indicating return of control to the main instruction flow (exit from the interrupt handler).ready_i— signal indicating the processor is ready to accept a trap (i.e., the processor is not currently inside a trap handler). This is bit zero of thereadysignal in the daisy chain. Whileready_iis zero, the daisy chain will not generate any interrupt signals.irq_o— signal indicating the start of interrupt processing.irq_cause_o— interrupt cause.irq_ret_o— signal indicating completion of interrupt request handling. It will correspond tocause_oat the moment themret_isignal appears.
The internal signal cause is the signal y from Fig. 1. As explained above, this signal can contain only one set bit, which corresponds to the accepted interrupt request. Therefore, this result can be used as a signal to identify the interrupt cause. The OR reduction (OR of all bits) of this signal yields the final interrupt request.
However, as mentioned in Lab #10, the RISC-V specification imposes certain requirements on the encoding of the mcause code for the interrupt cause. In particular, the most significant bit must be set to one, and the value of the remaining bits must be greater than 16. From a circuit design perspective, this is most easily implemented by concatenation: {12'h800, cause, 4'b0000} — in this case the most significant bit will be one, and if any bit of cause is one (which is the criterion for an interrupt occurring), the lower 31 bits of mcause will be greater than 16.
The register in Fig. 2 stores the value of the internal cause signal so that upon completion of the interrupt, a one is asserted on the corresponding bit of the irq_ret_o signal, notifying the device whose interrupt was being handled that processing has finished.
Assignment
- Implement the
daisy_chainmodule. - Integrate
daisy_chaininto theinterrupt_controllermodule according to the scheme shown in Fig. 3. - Reflect the changes to the
interrupt_controllersignal prototype in theprocessor_coreandprocessor_systemmodules.
Figure 3. Block diagram of the priority interrupt unit.
Important
Note that the bit width of signals
irq_req_i,mie_i, andirq_ret_ohas changed. These are now 16-bit signals. The signal that previously went to theirq_ret_ooutput now goes to theirq_ret_iinput of thedaisy_chainmodule. The generation of the interrupt cause codeirq_cause_ohas been moved into thedaisy_chainmodule.
Steps
- Implement the
daisy_chainmodule.- When forming the top array of AND gates from Fig. 2, you need to generate 16 continuous assignments using a
generate forblock. - The bottom array of AND gates can be formed using a single continuous assignment via the bitwise AND operation.
- When forming the top array of AND gates from Fig. 2, you need to generate 16 continuous assignments using a
- Verify the
daisy_chainmodule using the verification environment provided in the filelab_12.tb_daisy_chain. If error messages appear in the TCL console, you need to find and fix them.- Before running the simulation, make sure the correct top-level module is selected in
Simulation Sources.
- Before running the simulation, make sure the correct top-level module is selected in
- Integrate the
daisy_chainmodule into theinterrupt_controllermodule according to the scheme shown in Fig. 3.- Make sure to update the bit width of signals
irq_req_i,mie_i, andirq_ret_oin theinterrupt_controllermodule. - Also update the bit width of signals
irq_req_iandirq_ret_oin theprocessor_coreandprocessor_systemmodules. - Additionally, you now need to use the upper 16 bits of the
miesignal instead of a single bit when connecting theinterrupt_controllermodule insideprocessor_core.
- Make sure to update the bit width of signals
- Verify using the testbench from Lab #11 that nothing was broken during integration.
