Switch branches/tags
Nothing to show
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
215 lines (194 sloc) 6.59 KB
// Filename: icontrol.v
// Project: Zip CPU -- a small, lightweight, RISC CPU soft core
// Purpose: An interrupt controller, for managing many interrupt sources.
// This interrupt controller started from the question of how best to
// design a simple interrupt controller. As such, it has a few nice
// qualities to it:
// 1. This is wishbone compliant
// 2. It sits on a 32-bit wishbone data bus
// 3. It only consumes one address on that wishbone bus.
// 4. There is no extra delays associated with reading this
// device.
// 5. Common operations can all be done in one clock.
// So, how shall this be used? First, the 32-bit word is broken down as
// follows:
// Bit 31 - This is the global interrupt enable bit. If set, interrupts
// will be generated and passed on as they come in.
// Bits 16-30 - These are specific interrupt enable lines. If set,
// interrupts from source (bit#-16) will be enabled.
// To set this line and enable interrupts from this source, write
// to the register with this bit set and the global enable set.
// To disable this line, write to this register with global enable
// bit not set, but this bit set. (Writing a zero to any of these
// bits has no effect, either setting or unsetting them.)
// Bit 15 - This is the any interrupt pin. If any interrupt is pending,
// this bit will be set.
// Bits 0-14 - These are interrupt bits. When set, an interrupt is
// pending from the corresponding source--regardless of whether
// it was enabled. (If not enabled, it won't generate an
// interrupt, but it will still register here.) To clear any
// of these bits, write a '1' to the corresponding bit. Writing
// a zero to any of these bits has no effect.
// The peripheral also sports a parameter, IUSED, which can be set
// to any value between 1 and (buswidth/2-1, or) 15 inclusive. This will
// be the number of interrupts handled by this routine. (Without the
// parameter, Vivado was complaining about unused bits. With it, we can
// keep the complaints down and still use the routine).
// To get access to more than 15 interrupts, chain these together, so
// that one interrupt controller device feeds another.
// Creator: Dan Gisselquist, Ph.D.
// Gisselquist Technology, LLC
// Copyright (C) 2015,2017-2018, Gisselquist Technology, LLC
// This program is free software (firmware): you can redistribute it and/or
// modify it under the terms of the GNU General Public License as published
// by the Free Software Foundation, either version 3 of the License, or (at
// your option) any later version.
// This program is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
// for more details.
// You should have received a copy of the GNU General Public License along
// with this program. (It's in the $(ROOT)/doc directory. Run make with no
// target there if the PDF file isn't present.) If not, see
// <> for a copy.
// License: GPL, v3, as defined and found on,
`default_nettype none
module icontrol(i_clk, i_reset, i_wr, i_data, o_data,
i_brd_ints, o_interrupt);
parameter IUSED = 15, BW=32;
input wire i_clk, i_reset;
input wire i_wr;
input wire [BW-1:0] i_data;
output reg [BW-1:0] o_data;
input wire [(IUSED-1):0] i_brd_ints;
output wire o_interrupt;
reg [(IUSED-1):0] r_int_state;
reg [(IUSED-1):0] r_int_enable;
wire [(IUSED-1):0] nxt_int_state;
reg r_interrupt, r_gie;
wire w_any;
assign nxt_int_state = (r_int_state|i_brd_ints);
initial r_int_state = 0;
always @(posedge i_clk)
if (i_reset)
r_int_state <= 0;
else if (i_wr)
r_int_state <= i_brd_ints | (r_int_state & (~i_data[(IUSED-1):0]));
r_int_state <= nxt_int_state;
initial r_int_enable = 0;
always @(posedge i_clk)
if (i_reset)
r_int_enable <= 0;
else if ((i_wr)&&(i_data[BW-1]))
r_int_enable <= r_int_enable | i_data[(16+IUSED-1):16];
else if ((i_wr)&&(!i_data[BW-1]))
r_int_enable <= r_int_enable & (~ i_data[(16+IUSED-1):16]);
initial r_gie = 1'b0;
always @(posedge i_clk)
if (i_reset)
r_gie <= 1'b0;
else if (i_wr)
r_gie <= i_data[BW-1];
assign w_any = ((r_int_state & r_int_enable) != 0);
initial r_interrupt = 1'b0;
always @(posedge i_clk)
if (i_reset)
r_interrupt <= 1'b0;
r_interrupt <= (r_gie)&&(w_any);
if (IUSED < 15)
always @(posedge i_clk)
o_data <= {
r_gie, { {(15-IUSED){1'b0}}, r_int_enable },
w_any, { {(15-IUSED){1'b0}}, r_int_state } };
end else begin
always @(posedge i_clk)
o_data <= { r_gie, r_int_enable, w_any, r_int_state };
end endgenerate
assign o_interrupt = r_interrupt;
// Make verilator happy
// verilator lint_off UNUSED
wire [30:0] unused;
assign unused = i_data[30:0];
// verilator lint_on UNUSED
`ifdef FORMAL
`define ASSUME assume
`define ASSUME assert
reg f_past_valid;
initial f_past_valid = 1'b0;
always @(posedge i_clk)
f_past_valid <= 1'b1;
initial `ASSUME(i_reset);
always @(*)
if (!f_past_valid)
always @(posedge i_clk)
if ((!f_past_valid)||($past(i_reset)))
assert(w_any == 0);
assert(r_interrupt == 0);
assert(r_gie == 0);
assert(r_int_enable == 0);
always @(posedge i_clk)
if ((f_past_valid)&&(!$past(i_reset)))
assert((r_int_state & $past(i_brd_ints))==$past(i_brd_ints));
always @(posedge i_clk)
if (((f_past_valid)&&(!$past(i_reset)))
&&($past(r_int_state) & $past(r_int_enable))
&&($past(r_gie)) )
always @(posedge i_clk)
if ((f_past_valid)&&($past(w_any))&&(!$past(i_wr))&&(!$past(i_reset)))
always @(posedge i_clk)
if ((f_past_valid)&&(!$past(r_gie)))
always @(posedge i_clk)
if ((f_past_valid)&&(!$past(w_any)))
always @(posedge i_clk)
if ((f_past_valid)&&(!$past(i_reset))&&($past(i_wr)))
// Interrupts cannot be cleared, unless they are set
assert(r_int_state == (($past(i_brd_ints))
assert(r_gie == $past(i_data[BW-1]));
end else if ((f_past_valid)&&(!$past(i_reset)))
assert(r_int_state == ($past(r_int_state)|$past(i_brd_ints)));
assert(r_gie == $past(r_gie));