UberDDR3/delete_later/rtl/cpu/icontrol.v

383 lines
11 KiB
Verilog

////////////////////////////////////////////////////////////////////////////////
//
// Filename: icontrol.v
// {{{
// Project: 10Gb Ethernet switch
//
// 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) 2023, Gisselquist Technology, LLC
// {{{
// This file is part of the ETH10G project.
//
// The ETH10G project contains free software and gateware, licensed under the
// Apache License, Version 2.0 (the "License"). You may not use this project,
// or this file, except in compliance with the License. You may obtain a copy
// of the License at
// }}}
// http://www.apache.org/licenses/LICENSE-2.0
// {{{
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.
//
////////////////////////////////////////////////////////////////////////////////
//
`default_nettype none
// }}}
module icontrol #(
// {{{
parameter IUSED = 12, DW=32
// }}}
) (
// {{{
input wire i_clk, i_reset,
input wire i_wb_cyc, i_wb_stb, i_wb_we,
input wire [DW-1:0] i_wb_data,
input wire [DW/8-1:0] i_wb_sel,
output wire o_wb_stall, o_wb_ack,
output reg [DW-1:0] o_wb_data,
input wire [(IUSED-1):0] i_brd_ints,
output reg o_interrupt
// }}}
);
// Local declarations
// {{{
reg [(IUSED-1):0] r_int_state;
reg [(IUSED-1):0] r_int_enable;
reg r_mie;
wire w_any;
wire wb_write, enable_ints, disable_ints;
// }}}
assign wb_write = (i_wb_stb)&&(i_wb_we);
assign enable_ints = (wb_write)&&( i_wb_data[15]);
assign disable_ints = (wb_write)&&(!i_wb_data[15]);
// r_int_state
// {{{
// First step: figure out which interrupts have triggered. An
// interrupt "triggers" when the incoming interrupt wire is high, and
// stays triggered until cleared by the bus.
initial r_int_state = 0;
always @(posedge i_clk)
if (i_reset)
r_int_state <= 0;
else if (wb_write)
r_int_state <= i_brd_ints
| (r_int_state & (~i_wb_data[(IUSED-1):0]));
else
r_int_state <= (r_int_state | i_brd_ints);
// }}}
// r_int_enable
// {{{
// Second step: determine which interrupts are enabled.
// Only interrupts that are enabled will be propagated forward on
// the global interrupt line.
initial r_int_enable = 0;
always @(posedge i_clk)
if (i_reset)
r_int_enable <= 0;
else if (enable_ints)
r_int_enable <= r_int_enable | i_wb_data[16 +: IUSED];
else if (disable_ints)
r_int_enable <= r_int_enable & (~ i_wb_data[16 +: IUSED]);
// }}}
// r_mie
// {{{
// Third step: The master (global) interrupt enable bit.
initial r_mie = 1'b0;
always @(posedge i_clk)
if (i_reset)
r_mie <= 1'b0;
else if (enable_ints && i_wb_data[DW-1])
r_mie <= 1'b1;
else if (disable_ints && i_wb_data[DW-1])
r_mie <= 1'b0;
// }}}
//
// Have "any" enabled interrupts triggered?
assign w_any = ((r_int_state & r_int_enable) != 0);
// o_interrupt
// {{{
// How then shall the interrupt wire be set?
initial o_interrupt = 1'b0;
always @(posedge i_clk)
if (i_reset)
o_interrupt <= 1'b0;
else
o_interrupt <= (r_mie)&&(w_any);
// }}}
// o_wb_data
// {{{
// Create the output data. Place this into the next clock, to keep
// it synchronous with w_any.
initial o_wb_data = 0;
always @(posedge i_clk)
begin
o_wb_data <= 0;
o_wb_data[31] <= r_mie;
o_wb_data[15] <= w_any;
o_wb_data[16 +: IUSED] <= r_int_enable;
o_wb_data[ 0 +: IUSED] <= r_int_state;
end
// }}}
assign o_wb_ack = i_wb_stb;
assign o_wb_stall = 1'b0;
// Make verilator happy
// {{{
// verilator coverage_off
// verilator lint_off UNUSED
generate if (IUSED < 15)
begin : UNUSED_INTS
wire unused_int;
assign unused_int = &{ 1'b0, i_wb_data[32-2:(16+IUSED)],
i_wb_data[16-2:IUSED] };
end endgenerate
wire unused;
assign unused = &{ 1'b0, i_wb_cyc, i_wb_sel };
// verilator lint_on UNUSED
// verilator coverage_on
// }}}
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
//
// Formal properties section
// {{{
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
`ifdef FORMAL
// {{{
`ifdef ICONTROL
`define ASSUME assume
`else
`define ASSUME assert
`endif
reg f_past_valid;
initial f_past_valid = 1'b0;
always @(posedge i_clk)
f_past_valid <= 1'b1;
// }}}
////////////////////////////////////////////////////////////////////////
//
// Reset handling
// {{{
////////////////////////////////////////////////////////////////////////
//
//
initial `ASSUME(i_reset);
always @(*)
if (!f_past_valid)
`ASSUME(i_reset);
always @(posedge i_clk)
if ((!f_past_valid)||($past(i_reset)))
begin
assert(r_int_state == 0);
assert(r_int_enable == 0);
assert(w_any == 0);
assert(o_interrupt == 0);
assert(r_mie == 0);
end
// }}}
////////////////////////////////////////////////////////////////////////
//
// Formal contract
// {{{
////////////////////////////////////////////////////////////////////////
//
//
// Rule #1: An interrupt should be able to set the r_int_state bits
//
always @(posedge i_clk)
if ((f_past_valid)&&(!$past(i_reset)))
assert((r_int_state & $past(i_brd_ints))==$past(i_brd_ints));
// Rule #2: An interrupt should be generated if received and enabled
//
// Make sure any enabled interrupt generates an outgoing interrupt
// ... assuming the master interrupt enable is true and the
// individual interrupt enable is true as well.
always @(posedge i_clk)
if (((f_past_valid)&&(!$past(i_reset)))
&&(|$past(r_int_state & r_int_enable))
&&($past(r_mie)) )
assert(o_interrupt);
// Rule #3: If the global interrupt enable bit is off, then no
// interrupts shall be asserted
//
always @(posedge i_clk)
if ((f_past_valid)&&(!$past(r_mie)))
assert(!o_interrupt);
// Rule #4: If no active interrupts are enabled, then no outgoing
// interrupt shall be asserted either
always @(posedge i_clk)
if ((f_past_valid)&&(0 == |$past(r_int_state & r_int_enable)))
assert(!o_interrupt);
// Bus rules
//
// Rule #5: It should be possible to disable one (or all) interrupts
always @(posedge i_clk)
if ((f_past_valid)&&($past(disable_ints)))
assert(($past({i_wb_data[31],i_wb_data[16 +: IUSED]})
& { r_mie, r_int_enable }) == 0);
// Rule #6: It should be possible to enable one (or all) interrupts
always @(posedge i_clk)
if ((f_past_valid)&&(!$past(i_reset))&&($past(enable_ints)))
assert(($past({i_wb_data[31],i_wb_data[16 +: IUSED]})
& { r_mie, r_int_enable })
== $past({i_wb_data[31],i_wb_data[16 +: IUSED]}));
// Rule #7: It shoule be possible to acknowledge an interrupt, and so
// deactivate it
always @(posedge i_clk)
if ((f_past_valid)&&(!$past(i_reset))&&($past(wb_write)))
assert(r_int_state == $past(i_brd_ints
| (r_int_state & ~i_wb_data[IUSED-1:0])));
// Rule #8: The interrupt enables should be stable without a write
always @(posedge i_clk)
if ((f_past_valid) && (!$past(i_reset)) && (!$past(wb_write)))
assert($stable({r_mie, r_int_enable}));
// }}}
////////////////////////////////////////////////////////////////////////
//
// Bus properties
// {{{
////////////////////////////////////////////////////////////////////////
//
wire [1:0] f_nreqs, f_nacks, f_outstanding;
reg past_stb;
always @(*)
if (i_wb_stb)
assume(i_wb_cyc);
always @(posedge i_clk)
if (!f_past_valid || $past(i_reset))
assume(!i_wb_cyc);
fwb_slave #(.DW(DW), .AW(1), .F_MAX_STALL(0), .F_MAX_ACK_DELAY(1),
.F_LGDEPTH(2), .F_MAX_REQUESTS(1), .F_OPT_MINCLOCK_DELAY(0))
fwb(i_clk, i_reset,
i_wb_cyc, i_wb_stb, i_wb_we,
1'b0, i_wb_data, 4'hf,
o_wb_ack, o_wb_stall, o_wb_data, 1'b0,
f_nreqs, f_nacks, f_outstanding);
always @(*)
assert(f_outstanding == 0);
// }}}
////////////////////////////////////////////////////////////////////////
//
// Other consistency logic
// {{{
////////////////////////////////////////////////////////////////////////
//
// Without a write or a reset, past interrupts should remain
// enabled.
always @(posedge i_clk)
if ((f_past_valid)&&(!$past(wb_write))&&(!$past(i_reset)))
begin
assert(($past(r_int_state)& ~r_int_state)==0);
assert((!$past(w_any)) || w_any);
end
// The outgoing interrupt should never be high unless w_any
// is also high
always @(posedge i_clk)
if ((f_past_valid)&&(!$past(w_any)))
assert(!o_interrupt);
// }}}
////////////////////////////////////////////////////////////////////////
//
// Cover properties
// {{{
////////////////////////////////////////////////////////////////////////
//
//
always @(posedge i_clk)
cover(o_interrupt);
always @(posedge i_clk)
if (!f_past_valid)
cover($fell(w_any) && $stable(r_int_enable));
always @(posedge i_clk)
if (f_past_valid)
begin
cover(!o_interrupt && $past(w_any));
cover(!o_interrupt && $past(r_mie) && $past(|r_int_state));
end
// }}}
`endif
// }}}
endmodule