summaryrefslogtreecommitdiff
path: root/rtl/wb2axip/axilgpio.v
diff options
context:
space:
mode:
Diffstat (limited to 'rtl/wb2axip/axilgpio.v')
-rw-r--r--rtl/wb2axip/axilgpio.v808
1 files changed, 808 insertions, 0 deletions
diff --git a/rtl/wb2axip/axilgpio.v b/rtl/wb2axip/axilgpio.v
new file mode 100644
index 0000000..0a6e90c
--- /dev/null
+++ b/rtl/wb2axip/axilgpio.v
@@ -0,0 +1,808 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+// Filename: axilgpio
+// {{{
+// Project: WB2AXIPSP: bus bridges and other odds and ends
+//
+// Purpose: A simple and basic AXI-lite input and output module.
+// Tristates are not supported internally, although output bits
+// may be used externally to create a tristate input. For example, when
+// driving an I2C controller, you might wish to do something like:
+//
+// assign i2c_scl = gpio_output[1] ? 1'bz : gpio_output[1];
+// assign i2c_sda = gpio_output[0] ? 1'bz : gpio_output[0];
+//
+// Or, as another example:
+//
+// assign generic_io = gpio_output[3] ? 1'bz : gpio_output[2];
+//
+// Registers:
+// 0: LOAD
+// Write to this register will overwrite the output data bits.
+// 4: SET
+// Writes to this register will set every output data bit where
+// the written bit is set.
+//
+// OUTPUT[k] = OLD BIT[k] || NEW_BIT[k]
+//
+// 8: CLEAR
+// Writes to this register will clear every output data bit where
+// the written bit is set.
+//
+// OUTPUT[k] = OLD BIT[k] && (!NEW_BIT[k])
+//
+// 12: TOGGLE
+// Writes to this register will toggle every output bit where
+// the bit written is set.
+//
+// OUTPUT[k] = OLD BIT[k] ^ NEW_BIT[k]
+//
+// The next four registers are present if (and only if) NIN > 0. If not,
+// the prior four registers will be repeated.
+//
+// 16: Input data (if NIN > 0)
+// This is the input data, following a two-register CDC.
+// 20: Input data toggle detection
+// A bit will be set in this register if ever the associated
+// input data bit toggles. To clear, write a '1' to the toggled
+// bit in this register.
+//
+// Bits from this register that are set will then create an
+// outgoing interrupt--provided they are not masked.
+//
+// 24: Input data interrupt mask
+// If any "mask" bit is set, then toggled data will not trigger
+// an interrupt.
+// 28: Input data interrupts active
+// This is the AND of toggled data and a clear interrupt mask bit.
+//
+// An output interrupt is generated if any of the bits in the interrupt
+// active register is high.
+//
+// Creator: Dan Gisselquist, Ph.D.
+// Gisselquist Technology, LLC
+//
+////////////////////////////////////////////////////////////////////////////////
+// }}}
+// Copyright (C) 2021-2024, Gisselquist Technology, LLC
+// {{{
+//
+// This file is part of the WB2AXIP project.
+//
+// The WB2AXIP 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 axilgpio #(
+ // {{{
+ //
+ // Size of the AXI-lite bus. These are fixed, since 1) AXI-lite
+ // is fixed at a width of 32-bits by Xilinx def'n, and 2) since
+ // we only ever have 4 configuration words.
+ parameter C_AXI_ADDR_WIDTH = 5,
+ localparam C_AXI_DATA_WIDTH = 32,
+ // OPT_SKIDBUFFER will increase throughput to 100% from 50%
+ parameter [0:0] OPT_SKIDBUFFER = 1'b1,
+ // OPT_LOWPOWER will force RDATA to zero if ever !RVALID
+ parameter [0:0] OPT_LOWPOWER = 0,
+ // NOUT : Number of output bits. Must be > 0
+ parameter NOUT = 30,
+ // NIN: Number of input bits. May be zero if desired.
+ parameter NIN = 5,
+ parameter [NOUT-1:0] DEFAULT_OUTPUT = 0
+ // }}}
+ ) (
+ // {{{
+ input wire S_AXI_ACLK,
+ input wire S_AXI_ARESETN,
+ // AXI-lite interface
+ // {{{
+ input wire S_AXI_AWVALID,
+ output wire S_AXI_AWREADY,
+ input wire [C_AXI_ADDR_WIDTH-1:0] S_AXI_AWADDR,
+ input wire [2:0] S_AXI_AWPROT,
+ //
+ input wire S_AXI_WVALID,
+ output wire S_AXI_WREADY,
+ input wire [C_AXI_DATA_WIDTH-1:0] S_AXI_WDATA,
+ input wire [C_AXI_DATA_WIDTH/8-1:0] S_AXI_WSTRB,
+ //
+ output wire S_AXI_BVALID,
+ input wire S_AXI_BREADY,
+ output wire [1:0] S_AXI_BRESP,
+ //
+ input wire S_AXI_ARVALID,
+ output wire S_AXI_ARREADY,
+ input wire [C_AXI_ADDR_WIDTH-1:0] S_AXI_ARADDR,
+ input wire [2:0] S_AXI_ARPROT,
+ //
+ output wire S_AXI_RVALID,
+ input wire S_AXI_RREADY,
+ output wire [C_AXI_DATA_WIDTH-1:0] S_AXI_RDATA,
+ output wire [1:0] S_AXI_RRESP,
+ // }}}
+ output wire [NOUT-1:0] o_gpio,
+ input wire [((NIN>0) ? (NIN-1):0):0] i_gpio,
+ output wire o_int
+ // }}}
+ );
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Register/wire signal declarations
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ localparam ADDRLSB = $clog2(C_AXI_DATA_WIDTH)-3;
+ localparam [2:0] // ADDR_LOAD = 3'b000,
+ // ADDR_SET = 3'b001,
+ // ADDR_CLEAR = 3'b010,
+ // ADDR_TOGGLE = 3'b011,
+ ADDR_INDATA = 3'b100,
+ ADDR_CHANGED= 3'b101,
+ ADDR_MASK = 3'b110,
+ ADDR_INT = 3'b111;
+
+
+ wire i_reset = !S_AXI_ARESETN;
+
+ wire axil_write_ready;
+ wire [C_AXI_ADDR_WIDTH-ADDRLSB-1:0] awskd_addr;
+ //
+ wire [C_AXI_DATA_WIDTH-1:0] wskd_data;
+ wire [C_AXI_DATA_WIDTH/8-1:0] wskd_strb;
+ reg axil_bvalid;
+ //
+ wire axil_read_ready;
+ wire [C_AXI_ADDR_WIDTH-ADDRLSB-1:0] arskd_addr;
+ reg [C_AXI_DATA_WIDTH-1:0] axil_read_data;
+ reg axil_read_valid;
+
+ reg [31:0] r_gpio;
+ wire [31:0] ck_gpio, ck_toggled, w_mask, int_toggled, wskd_gpio;
+
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // AXI-lite signaling
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ //
+ // Write signaling
+ //
+ // {{{
+
+ generate if (OPT_SKIDBUFFER)
+ begin : SKIDBUFFER_WRITE
+ // {{{
+ wire awskd_valid, wskd_valid;
+
+ skidbuffer #(
+ // {{{
+ .OPT_OUTREG(0),
+ .OPT_LOWPOWER(OPT_LOWPOWER),
+ .DW(C_AXI_ADDR_WIDTH-ADDRLSB)
+ // }}}
+ ) axilawskid(
+ // {{{
+ .i_clk(S_AXI_ACLK), .i_reset(i_reset),
+ .i_valid(S_AXI_AWVALID), .o_ready(S_AXI_AWREADY),
+ .i_data(S_AXI_AWADDR[C_AXI_ADDR_WIDTH-1:ADDRLSB]),
+ .o_valid(awskd_valid), .i_ready(axil_write_ready),
+ .o_data(awskd_addr)
+ // }}}
+ );
+
+ skidbuffer #(
+ // {{{
+ .OPT_OUTREG(0),
+ .OPT_LOWPOWER(OPT_LOWPOWER),
+ .DW(C_AXI_DATA_WIDTH+C_AXI_DATA_WIDTH/8)
+ // }}}
+ ) axilwskid(
+ // {{{
+ .i_clk(S_AXI_ACLK), .i_reset(i_reset),
+ .i_valid(S_AXI_WVALID), .o_ready(S_AXI_WREADY),
+ .i_data({ S_AXI_WDATA, S_AXI_WSTRB }),
+ .o_valid(wskd_valid), .i_ready(axil_write_ready),
+ .o_data({ wskd_data, wskd_strb })
+ // }}}
+ );
+
+ assign axil_write_ready = awskd_valid && wskd_valid
+ && (!S_AXI_BVALID || S_AXI_BREADY);
+ // }}}
+ end else begin : SIMPLE_WRITES
+ // {{{
+ reg axil_awready;
+
+ initial axil_awready = 1'b0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ axil_awready <= 1'b0;
+ else
+ axil_awready <= !axil_awready
+ && (S_AXI_AWVALID && S_AXI_WVALID)
+ && (!S_AXI_BVALID || S_AXI_BREADY);
+
+ assign S_AXI_AWREADY = axil_awready;
+ assign S_AXI_WREADY = axil_awready;
+
+ assign awskd_addr = S_AXI_AWADDR[C_AXI_ADDR_WIDTH-1:ADDRLSB];
+ assign wskd_data = S_AXI_WDATA;
+ assign wskd_strb = S_AXI_WSTRB;
+
+ assign axil_write_ready = axil_awready;
+ // }}}
+ end endgenerate
+
+ initial axil_bvalid = 0;
+ always @(posedge S_AXI_ACLK)
+ if (i_reset)
+ axil_bvalid <= 0;
+ else if (axil_write_ready)
+ axil_bvalid <= 1;
+ else if (S_AXI_BREADY)
+ axil_bvalid <= 0;
+
+ assign S_AXI_BVALID = axil_bvalid;
+ assign S_AXI_BRESP = 2'b00;
+ // }}}
+
+ //
+ // Read signaling
+ //
+ // {{{
+
+ generate if (OPT_SKIDBUFFER)
+ begin : SKIDBUFFER_READ
+ // {{{
+ wire arskd_valid;
+
+ skidbuffer #(.OPT_OUTREG(0),
+ .OPT_LOWPOWER(OPT_LOWPOWER),
+ .DW(C_AXI_ADDR_WIDTH-ADDRLSB))
+ axilarskid(//
+ .i_clk(S_AXI_ACLK), .i_reset(i_reset),
+ .i_valid(S_AXI_ARVALID), .o_ready(S_AXI_ARREADY),
+ .i_data(S_AXI_ARADDR[C_AXI_ADDR_WIDTH-1:ADDRLSB]),
+ .o_valid(arskd_valid), .i_ready(axil_read_ready),
+ .o_data(arskd_addr));
+
+ assign axil_read_ready = arskd_valid
+ && (!axil_read_valid || S_AXI_RREADY);
+ // }}}
+ end else begin : SIMPLE_READS
+ // {{{
+ reg axil_arready;
+
+ always @(*)
+ axil_arready = !S_AXI_RVALID;
+
+ assign arskd_addr = S_AXI_ARADDR[C_AXI_ADDR_WIDTH-1:ADDRLSB];
+ assign S_AXI_ARREADY = axil_arready;
+ assign axil_read_ready = (S_AXI_ARVALID && S_AXI_ARREADY);
+ // }}}
+ end endgenerate
+
+ initial axil_read_valid = 1'b0;
+ always @(posedge S_AXI_ACLK)
+ if (i_reset)
+ axil_read_valid <= 1'b0;
+ else if (axil_read_ready)
+ axil_read_valid <= 1'b1;
+ else if (S_AXI_RREADY)
+ axil_read_valid <= 1'b0;
+
+ assign S_AXI_RVALID = axil_read_valid;
+ assign S_AXI_RDATA = axil_read_data;
+ assign S_AXI_RRESP = 2'b00;
+ // }}}
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // AXI-lite register logic
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ assign wskd_gpio = apply_wstrb(r_gpio, wskd_data, wskd_strb);
+
+ // r_gpio, o_gpio
+ // {{{
+ initial r_gpio = 0;
+ always @(posedge S_AXI_ACLK)
+ begin
+ if (axil_write_ready)
+ begin
+ case({ (awskd_addr[2] && (NIN > 0)), awskd_addr[1:0] })
+ 3'b000: r_gpio <= wskd_gpio;
+ 3'b001: begin // SET
+ // {{{
+ if (wskd_strb[0])
+ r_gpio[ 7: 0]<= r_gpio[ 7: 0]| wskd_data[ 7: 0];
+ if (wskd_strb[1])
+ r_gpio[15: 8]<= r_gpio[15: 8]| wskd_data[15: 8];
+ if (wskd_strb[2])
+ r_gpio[23:16]<= r_gpio[23:16]| wskd_data[23:16];
+ if (wskd_strb[3])
+ r_gpio[31:24]<= r_gpio[31:24]| wskd_data[31:24];
+ end
+ // }}}
+ 3'b010: begin // CLEAR
+ // {{{
+ if (wskd_strb[0])
+ r_gpio[ 7: 0]<= r_gpio[ 7: 0]&~wskd_data[ 7: 0];
+ if (wskd_strb[1])
+ r_gpio[15: 8]<= r_gpio[15: 8]&~wskd_data[15: 8];
+ if (wskd_strb[2])
+ r_gpio[23:16]<= r_gpio[23:16]&~wskd_data[23:16];
+ if (wskd_strb[3])
+ r_gpio[31:24]<= r_gpio[31:24]&~wskd_data[31:24];
+ end
+ // }}}
+ 3'b011: begin // TOGGLE
+ // {{{
+ if (wskd_strb[0])
+ r_gpio[ 7: 0]<=r_gpio[ 7: 0] ^ wskd_data[ 7: 0];
+ if (wskd_strb[1])
+ r_gpio[15: 8]<=r_gpio[15: 8] ^ wskd_data[15: 8];
+ if (wskd_strb[2])
+ r_gpio[23:16]<=r_gpio[23:16] ^ wskd_data[23:16];
+ if (wskd_strb[3])
+ r_gpio[31:24]<=r_gpio[31:24] ^ wskd_data[31:24];
+ end
+ // }}}
+ default: begin end // Input registers
+ endcase
+ end
+
+ if (i_reset)
+ r_gpio[NOUT-1:0] <= DEFAULT_OUTPUT;
+ if (NOUT < 32)
+ r_gpio[31:((NOUT < 32) ? NOUT : 31)] <= 0;
+ end
+
+ assign o_gpio = r_gpio[NOUT-1:0];
+ // }}}
+
+ // i_gpio -> ck_gpio, $changed(i_gpio) -> ck_toggled, r_mask
+ // {{{
+ generate if (NIN > 0)
+ begin : INPUT_HANDLING
+ // {{{
+ // Poss interrupts: Toggle, Rise, Fall
+ // Only toggle is implemented here
+ reg [NIN-1:0] last_gpio, qq_gpio, q_gpio, toggled,
+ r_mask;
+ wire [31:0] wstrb_mask;
+ reg r_int;
+ integer ik;
+
+ // r_int
+ // {{{
+ initial r_int = 0;
+ always @(posedge S_AXI_ACLK)
+ if (i_reset)
+ r_int <= 0;
+ else
+ r_int <= |(r_mask & toggled);
+ // }}}
+
+ // Two clock CDC: last_gpio, qq_gpio, q_gpio
+ // {{{
+ initial { last_gpio, qq_gpio, q_gpio } = 0;
+ always @(posedge S_AXI_ACLK)
+ if (i_reset)
+ { last_gpio, qq_gpio, q_gpio } <= 0;
+ else
+ { last_gpio, qq_gpio, q_gpio }
+ <= { qq_gpio, q_gpio, i_gpio };
+ // }}}
+
+ // Toggled
+ // {{{
+ initial toggled = 0;
+ always @(posedge S_AXI_ACLK)
+ if (i_reset)
+ toggled <= 0;
+ else begin
+ for(ik=0; ik<NIN; ik=ik+1)
+ begin
+ if (axil_write_ready && awskd_addr == 3'b101
+ && wskd_strb[ik/8] && wskd_data[ik])
+ toggled[ik] <= 1'b0;
+ if (last_gpio[ik] ^ qq_gpio[ik])
+ toggled[ik] <= 1'b1;
+ end
+ end
+ // }}}
+
+ // r_mask
+ // {{{
+ assign wstrb_mask = apply_wstrb(w_mask, wskd_data, wskd_strb);
+
+ initial r_mask = 0;
+ always @(posedge S_AXI_ACLK)
+ if (i_reset)
+ r_mask <= 0;
+ else if (axil_write_ready && awskd_addr == 3'b110)
+ r_mask <= wstrb_mask[NIN-1:0];
+ // }}}
+
+ assign ck_gpio = { {(32-NIN){1'b0}}, qq_gpio };
+ assign ck_toggled = { {(32-NIN){1'b0}}, toggled };
+ assign w_mask = { {(32-NIN){1'b0}}, r_mask };
+ assign int_toggled = { {(32-NIN){1'b0}}, (r_mask & toggled) };
+ assign o_int = r_int;
+
+ // Make Verilator happy
+ // {{{
+ // Verilator lint_off UNUSED
+ wire unused_inputs;
+ assign unused_inputs = &{ 1'b0, wstrb_mask[31:NIN] };
+ // Verilator lint_on UNUSED
+ // }}}
+ // }}}
+ end else begin : NO_INPUTS
+ // {{{
+ assign ck_gpio = 32'h0;
+ assign ck_toggled = 32'h0;
+ assign w_mask = 32'h0;
+ assign int_toggled = 32'h0;
+ assign o_int = 1'b0;
+
+ // Make Verilator happy
+ // {{{
+ // Verilator lint_off UNUSED
+ wire unused_inputs;
+ assign unused_inputs = &{ 1'b0, i_gpio };
+ // Verilator lint_on UNUSED
+ // }}}
+ // }}}
+ end endgenerate
+ // }}}
+
+ // axil_read_data
+ // {{{
+ initial axil_read_data = 0;
+ always @(posedge S_AXI_ACLK)
+ if (OPT_LOWPOWER && !S_AXI_ARESETN)
+ axil_read_data <= 0;
+ else if (!S_AXI_RVALID || S_AXI_RREADY)
+ begin
+ axil_read_data <= 0;
+
+ casez({ ((NIN>0)&& arskd_addr[2]), arskd_addr[1:0] })
+ 3'b0??: axil_read_data[NOUT-1:0] <= r_gpio[NOUT-1:0];
+ ADDR_INDATA: axil_read_data <= ck_gpio;
+ ADDR_CHANGED: axil_read_data <= ck_toggled;
+ ADDR_MASK: axil_read_data <= w_mask;
+ ADDR_INT: axil_read_data <= int_toggled;
+ endcase
+
+ if (OPT_LOWPOWER && !axil_read_ready)
+ axil_read_data <= 0;
+ end
+ // }}}
+
+ function [C_AXI_DATA_WIDTH-1:0] apply_wstrb;
+ // {{{
+ input [C_AXI_DATA_WIDTH-1:0] prior_data;
+ input [C_AXI_DATA_WIDTH-1:0] new_data;
+ input [C_AXI_DATA_WIDTH/8-1:0] wstrb;
+
+ integer k;
+ for(k=0; k<C_AXI_DATA_WIDTH/8; k=k+1)
+ begin
+ apply_wstrb[k*8 +: 8]
+ = wstrb[k] ? new_data[k*8 +: 8] : prior_data[k*8 +: 8];
+ end
+ endfunction
+ // }}}
+ // }}}
+
+ // Make Verilator happy
+ // {{{
+ // Verilator lint_off UNUSED
+ wire unused;
+ assign unused = &{ 1'b0, S_AXI_AWPROT, S_AXI_ARPROT,
+ S_AXI_ARADDR[ADDRLSB-1:0],
+ S_AXI_AWADDR[ADDRLSB-1:0] };
+ // Verilator lint_on UNUSED
+ // }}}
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+//
+// Formal properties
+// {{{
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+`ifdef FORMAL
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // The AXI-lite control interface
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ localparam F_AXIL_LGDEPTH = 4;
+ wire [F_AXIL_LGDEPTH-1:0] faxil_rd_outstanding,
+ faxil_wr_outstanding,
+ faxil_awr_outstanding;
+
+ faxil_slave #(
+ // {{{
+ .C_AXI_DATA_WIDTH(C_AXI_DATA_WIDTH),
+ .C_AXI_ADDR_WIDTH(C_AXI_ADDR_WIDTH),
+ .F_LGDEPTH(F_AXIL_LGDEPTH),
+ .F_AXI_MAXWAIT(3),
+ .F_AXI_MAXDELAY(3),
+ .F_AXI_MAXRSTALL(5),
+ .F_OPT_COVER_BURST(4)
+ // }}}
+ ) faxil(
+ // {{{
+ .i_clk(S_AXI_ACLK), .i_axi_reset_n(S_AXI_ARESETN),
+ //
+ .i_axi_awvalid(S_AXI_AWVALID),
+ .i_axi_awready(S_AXI_AWREADY),
+ .i_axi_awaddr( S_AXI_AWADDR),
+ .i_axi_awprot( S_AXI_AWPROT),
+ //
+ .i_axi_wvalid(S_AXI_WVALID),
+ .i_axi_wready(S_AXI_WREADY),
+ .i_axi_wdata( S_AXI_WDATA),
+ .i_axi_wstrb( S_AXI_WSTRB),
+ //
+ .i_axi_bvalid(S_AXI_BVALID),
+ .i_axi_bready(S_AXI_BREADY),
+ .i_axi_bresp( S_AXI_BRESP),
+ //
+ .i_axi_arvalid(S_AXI_ARVALID),
+ .i_axi_arready(S_AXI_ARREADY),
+ .i_axi_araddr( S_AXI_ARADDR),
+ .i_axi_arprot( S_AXI_ARPROT),
+ //
+ .i_axi_rvalid(S_AXI_RVALID),
+ .i_axi_rready(S_AXI_RREADY),
+ .i_axi_rdata( S_AXI_RDATA),
+ .i_axi_rresp( S_AXI_RRESP),
+ //
+ .f_axi_rd_outstanding(faxil_rd_outstanding),
+ .f_axi_wr_outstanding(faxil_wr_outstanding),
+ .f_axi_awr_outstanding(faxil_awr_outstanding)
+ // }}}
+ );
+
+ always @(*)
+ if (OPT_SKIDBUFFER)
+ begin
+ assert(faxil_awr_outstanding== (S_AXI_BVALID ? 1:0)
+ +(S_AXI_AWREADY ? 0:1));
+ assert(faxil_wr_outstanding == (S_AXI_BVALID ? 1:0)
+ +(S_AXI_WREADY ? 0:1));
+
+ assert(faxil_rd_outstanding == (S_AXI_RVALID ? 1:0)
+ +(S_AXI_ARREADY ? 0:1));
+ end else begin
+ assert(faxil_wr_outstanding == (S_AXI_BVALID ? 1:0));
+ assert(faxil_awr_outstanding == faxil_wr_outstanding);
+
+ assert(faxil_rd_outstanding == (S_AXI_RVALID ? 1:0));
+ end
+
+ //
+ // Check that our low-power only logic works by verifying that anytime
+ // S_AXI_RVALID is inactive, then the outgoing data is also zero.
+ //
+ always @(*)
+ if (OPT_LOWPOWER && !S_AXI_RVALID)
+ assert(S_AXI_RDATA == 0);
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Register return checking
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+`define CHECK_REGISTERS
+`ifdef CHECK_REGISTERS
+ (* anyconst *) reg [$clog2(NOUT)-1:0] f_obit;
+
+ always @(*)
+ assume(f_obit < NOUT);
+
+ // Verify o_gpio
+ // {{{
+ always @(posedge S_AXI_ACLK)
+ if ($past(i_reset) || i_reset)
+ begin
+ if ($past(i_reset))
+ assert(o_gpio[f_obit] == DEFAULT_OUTPUT[f_obit]);
+ end else if ($past(axil_write_ready && wskd_strb[f_obit/8]))
+ begin
+ case($past(awskd_addr[2:0]))
+ 3'b000: assert(o_gpio[f_obit] == $past(wskd_data[f_obit]));
+ 3'b001: assert(o_gpio[f_obit] == $past(o_gpio[f_obit] || wskd_data[f_obit]));
+ 3'b010: assert(o_gpio[f_obit] == $past(o_gpio[f_obit] && !wskd_data[f_obit]));
+ 3'b011: assert(o_gpio[f_obit] == $past(o_gpio[f_obit] ^ wskd_data[f_obit]));
+ default: begin end
+ endcase
+ end
+
+ faxil_register #(
+ // {{{
+ .AW(C_AXI_ADDR_WIDTH),
+ .DW(C_AXI_DATA_WIDTH),
+ .ADDR({ 3'b000, {(ADDRLSB){1'b0}} }),
+ .MASK({(C_AXI_DATA_WIDTH){1'b0}})
+ // }}}
+ ) foutputs (
+ // {{{
+ .S_AXI_ACLK(S_AXI_ACLK),
+ .S_AXI_ARESETN(S_AXI_ARESETN),
+ .S_AXIL_AWW(axil_write_ready),
+ .S_AXIL_AWADDR({ awskd_addr[2], {(ADDRLSB+2){1'b0}} }),
+ .S_AXIL_WDATA(wskd_data),
+ .S_AXIL_WSTRB(wskd_strb),
+ .S_AXIL_BVALID(S_AXI_BVALID),
+ .S_AXIL_AR(axil_read_ready),
+ .S_AXIL_ARADDR({ arskd_addr[2], {(ADDRLSB+2){1'b0}} }),
+ .S_AXIL_RVALID(S_AXI_RVALID),
+ .S_AXIL_RDATA(S_AXI_RDATA),
+ .i_register(o_gpio)
+ // }}}
+ );
+
+ // }}}
+
+ generate if (NIN > 0)
+ begin : CHECK_INPUT
+ (* anyconst *) reg [$clog2(NIN)-1:0] f_ibit;
+
+ always @(*)
+ assume(f_ibit < NIN);
+
+ always @(posedge S_AXI_ACLK)
+ if ($past(i_reset) || i_reset)
+ begin
+ if ($past(i_reset))
+ assert(!ck_gpio[f_ibit]);
+ end else begin
+ if (!$past(i_reset, 2)
+ && $past(ck_gpio[f_ibit]) != $past(ck_gpio[f_ibit],2))
+ assert(ck_toggled[f_ibit]);
+ else if ($past(axil_write_ready
+ && awskd_addr == ADDR_CHANGED
+ && wskd_strb[f_ibit/8]
+ && wskd_data[f_ibit]))
+ assert(!ck_toggled[f_ibit]);
+ end
+
+ faxil_register #(
+ // {{{
+ .AW(C_AXI_ADDR_WIDTH),
+ .DW(C_AXI_DATA_WIDTH),
+ .ADDR({ ADDR_INDATA, {(ADDRLSB){1'b0}} }),
+ .MASK({(C_AXI_DATA_WIDTH){1'b0}})
+ // }}}
+ ) finputs (
+ // {{{
+ .S_AXI_ACLK(S_AXI_ACLK),
+ .S_AXI_ARESETN(S_AXI_ARESETN),
+ .S_AXIL_AWW(axil_write_ready),
+ .S_AXIL_AWADDR({ awskd_addr, {(ADDRLSB){1'b0}} }),
+ .S_AXIL_WDATA(wskd_data),
+ .S_AXIL_WSTRB(wskd_strb),
+ .S_AXIL_BVALID(S_AXI_BVALID),
+ .S_AXIL_AR(axil_read_ready),
+ .S_AXIL_ARADDR({ arskd_addr, {(ADDRLSB){1'b0}} }),
+ .S_AXIL_RVALID(S_AXI_RVALID),
+ .S_AXIL_RDATA(S_AXI_RDATA),
+ .i_register(ck_gpio)
+ // }}}
+ );
+
+ faxil_register #(
+ // {{{
+ .AW(C_AXI_ADDR_WIDTH),
+ .DW(C_AXI_DATA_WIDTH),
+ .ADDR({ ADDR_CHANGED, {(ADDRLSB){1'b0}} }),
+ .MASK({(C_AXI_DATA_WIDTH){1'b0}})
+ // }}}
+ ) ftoggled (
+ // {{{
+ .S_AXI_ACLK(S_AXI_ACLK),
+ .S_AXI_ARESETN(S_AXI_ARESETN),
+ .S_AXIL_AWW(axil_write_ready),
+ .S_AXIL_AWADDR({ awskd_addr, {(ADDRLSB){1'b0}} }),
+ .S_AXIL_WDATA(wskd_data),
+ .S_AXIL_WSTRB(wskd_strb),
+ .S_AXIL_BVALID(S_AXI_BVALID),
+ .S_AXIL_AR(axil_read_ready),
+ .S_AXIL_ARADDR({ arskd_addr, {(ADDRLSB){1'b0}} }),
+ .S_AXIL_RVALID(S_AXI_RVALID),
+ .S_AXIL_RDATA(S_AXI_RDATA),
+ .i_register(ck_toggled)
+ // }}}
+ );
+
+ faxil_register #(
+ // {{{
+ .AW(C_AXI_ADDR_WIDTH),
+ .DW(C_AXI_DATA_WIDTH),
+ .ADDR({ ADDR_MASK, {(ADDRLSB){1'b0}} }),
+ .MASK({ {(C_AXI_DATA_WIDTH-NIN){1'b0}}, {(NIN){1'b1}} })
+ // }}}
+ ) fmask (
+ // {{{
+ .S_AXI_ACLK(S_AXI_ACLK),
+ .S_AXI_ARESETN(S_AXI_ARESETN),
+ .S_AXIL_AWW(axil_write_ready),
+ .S_AXIL_AWADDR({ awskd_addr, {(ADDRLSB){1'b0}} }),
+ .S_AXIL_WDATA(wskd_data),
+ .S_AXIL_WSTRB(wskd_strb),
+ .S_AXIL_BVALID(S_AXI_BVALID),
+ .S_AXIL_AR(axil_read_ready),
+ .S_AXIL_ARADDR({ arskd_addr, {(ADDRLSB){1'b0}} }),
+ .S_AXIL_RVALID(S_AXI_RVALID),
+ .S_AXIL_RDATA(S_AXI_RDATA),
+ .i_register(w_mask)
+ // }}}
+ );
+ end endgenerate
+`endif
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Induction checks
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Cover checks
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ always @(posedge S_AXI_ACLK)
+ if (!i_reset)
+ cover(o_int);
+
+ always @(posedge S_AXI_ACLK)
+ if (!i_reset)
+ cover($fell(o_int));
+
+ // }}}
+`endif
+// }}}
+endmodule