Skip to content

Interrupts, Glue logic, and an example application

In this chapter, we will do for interrupts what we have done for polling in a previous chapter.  Namely, we will write a SystemVerilog module which connects the three SystemVerilog modules for

  • Bird CPU
  • a 4×7 segment display as an output device,
  • a switchbank as an input device

A switchbank that can send interrupts

Note that a switchbank working with interrupts does not have an a0 signal, as it has only a single register: a data register. It does not need a status register.

Note also that the interrupt signal works exactly like the least significant bit of the status register in the polling case. It is set when enter key is pressed and reset when the switchbank is read by the CPU (ie, when ack becomes 1)

module switchbank_int (
input clk,
//--user side
input [15:0]switches ,
input enter_key,
//--cpu side
input ack,
output interrupt,
output [15:0]data_reg
) ; 

logic [1:0]pressed; 
always_ff @(posedge clk)
begin
pressed <= {pressed[0],enter_key};
if ( ( pressed == 2'b10 ) && ( interrupt == 1'b0 ) )
begin
interrupt <= 1'b1;
data_reg <= switches;
end
else if ( ack && ( interrupt == 1'b1 ) )
interrupt <=1'b0;
end

initial begin
data_reg = 16'h0000;
end 
endmodule

Top module (or glue logic)

In our design, glue logic will have some additional duties compared to the polling case. It will again implement the memory map. But in addition to that, it will implement the PIC, or the programmable interrupt controller.

module main_module (
                        input clk,
                        //---input from switchbank
                        input [7:0]  switches,            //input from 16-bit switchboard
                        input  enter_key,               //enter button
                        //---output to seven segment display
                        output logic [3:0] grounds,
                        output logic [6:0] display
                   );

//====memory map is defined here====
localparam    BEGINMEM = 12'h000,
              ENDMEM = 12'h7ff,
              SWITCHBANK = 12'h900,               
              SEVENSEG = 12'hb00;

//====memory chip==============
logic [15:0] memory [0:127]; 
 
//=====cpu's input-output pins=====
logic [15:0] data_out;
logic [15:0] data_in;
logic [11:0] address;
logic memwt;
logic INT;    //interrupt pin
logic intack; //interrupt acknowledgement

//======ss7 and switchbank=====
logic [15:0] ss7_out, switch_in;
//====== pic ===============
logic irq0, irq1, irq2, irq3, irq4, irq5, irq6, irq7;

//=====components==================
sevensegment ss1 (.datain(ss7_out), .grounds(grounds), .display(display), .clk(clk));

switchbank_int  sw1(.clk(clk), .switches(16'(switches)), .enter_key(enter_key),  .ack(ackx) , .interrupt(switch_interrupt),.data_reg(switch_in));
mammal m1( .clk(clk), .data_in(data_in), .data_out(data_out), .address(address), .memwt(memwt),.INT(INT), .intack(intack));



//===============IRQ's==============
always_comb
    begin
      irq0 = 1'b0;
      irq1 = 1'b0;
      irq2 = switch_interrupt;
      irq3 = 1'b0;
      irq4 = 1'b0;
      irq5 = 1'b0;
      irq6 = 1'b0;
      irq7 = 1'b0;
   end

//we assume that the devices hold their irq until being serviced by cpu
assign INT = irq0 | irq1 | irq2 | irq3 | irq4 | irq5 | irq6 | irq7; 

//====multiplexer for cpu input======
always_comb
begin
ackx = 0;
        if (intack == 0)
        begin
            ackx = 0;
            if ( (BEGINMEM<=address) && (address<=ENDMEM) )
                    data_in=memory[address];
            else if (address==SWITCHBANK)
                    begin
                         ackx = 1;              //with appropriate a0 resets the ready flag    
                         data_in = switch_in;   //a0 will determine if we read data or status
                    end
            else
                      data_in=16'hf345; //last else to generate combinatorial circuit.
                
         end
         else                        //intack = 1
            begin
             if (irq0)               //highest priority interrupt is irq0
                 data_in = 16'h0;
             else if (irq1)
                 data_in = 16'h1;
             else if (irq2)
                 data_in = 16'h2;
             else if (irq3)
                 data_in = 16'h3;
             else if (irq4)
                 data_in = 16'h4;
             else if (irq5)
                 data_in = 16'h5;
             else if (irq6)
                 data_in = 16'h6;
             else                           //  irq7 
                 data_in = 16'h7;
            end
end

//=====multiplexer for cpu output=========== 
always_ff @(posedge clk) //data output port of the cpu
    if (memwt)
        if ( (BEGINMEM<=address) && (address<=ENDMEM) )
               memory[address]<=data_out;
        else if ( SEVENSEG==address ) 
               ss7_out<=data_out;



initial 
    begin
         switch_interrupt =0;
         ss7_out =16'b0;
        $readmemh("ram.dat", memory);
    end
endmodule

A simple assembly language program which manages the interrupt signal is given below: You can use this program as a test to see whether everything in hardware works properly.

      ldi 7 0x600
      ldi 0 0x7f2
      ldi 1 isr
      st  0 1
      sti
loop  jmp loop
isr   ldi 2 0x900
      ld 2 2
      ldi 3 0xb00
      st 3 2
      sti
      iret