blob: 2b7f1332f61c26c3ce3bdb416b042b57a482e92d [file] [log] [blame]
////////////////////////////////////////////////////////////////////////////////
//
// Filename: axiluart
// {{{
// Project: wbuart32, a full featured UART with simulator
//
// Purpose: A basic AXI-Lite serial port controller. It has the same
// interface as the WBUART core in the same directory.
//
// Creator: Dan Gisselquist, Ph.D.
// Gisselquist Technology, LLC
//
////////////////////////////////////////////////////////////////////////////////
// }}}
// Copyright (C) 2020-2021, 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
// <http://www.gnu.org/licenses/> for a copy.
//
// License: GPL, v3, as defined and found on www.gnu.org,
// http://www.gnu.org/licenses/gpl.html
//
//
////////////////////////////////////////////////////////////////////////////////
// }}}
//
`default_nettype none
//
module axiluart #(
// {{{
// 4MB 8N1, when using 100MHz clock
parameter [30:0] INITIAL_SETUP = 31'd25,
//
// LGFLEN: The log (based two) of our FIFOs size. Maxes out
// at 10, representing a FIFO length of 1024.
parameter [3:0] LGFLEN = 4,
//
// HARDWARE_FLOW_CONTROL_PRESET controls whether or not we
// ignore the RTS/CTS signaling. If present, we only start
// transmitting if
parameter [0:0] HARDWARE_FLOW_CONTROL_PRESENT = 1'b1,
// Perform a simple/quick bounds check on the log FIFO length,
// to make sure its within the bounds we can support with our
// current interface.
localparam [3:0] LCLLGFLEN = (LGFLEN > 4'ha)? 4'ha
: ((LGFLEN < 4'h2) ? 4'h2 : LGFLEN),
//
// 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 = 4,
localparam C_AXI_DATA_WIDTH = 32,
parameter [0:0] OPT_SKIDBUFFER = 1'b0,
parameter [0:0] OPT_LOWPOWER = 0,
localparam ADDRLSB = $clog2(C_AXI_DATA_WIDTH)-3
// }}}
) (
// AXI-lite signaling
// {{{
input wire S_AXI_ACLK,
input wire S_AXI_ARESETN,
//
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,
// }}}
// UART signals
// {{{
input wire i_uart_rx,
output wire o_uart_tx,
//
// CTS is the "Clear-to-send" hardware flow control signal. We
// set it anytime our FIFO isn't full. Feel free to ignore
// this output if you do not wish to use flow control.
input wire i_cts_n,
//
// RTS is used for hardware flow control. According to
// Wikipedia, it should probably be renamed RTR for "ready to
// receive". It tell us whether or not the receiving hardware
// is ready to accept another byte. If low, the transmitter
// will pause.
//
// If you don't wish to use hardware flow control, just set
// HARDWARE_FLOW_CONTROL_PRESENT to 1'b0 and let the optimizer
// simply remove this logic.
output reg o_rts_n,
// }}}
// A series of outgoing interrupts to select from among
// {{{
output wire o_uart_rx_int,
output wire o_uart_tx_int,
output wire o_uart_rxfifo_int,
output wire o_uart_txfifo_int
// }}}
);
////////////////////////////////////////////////////////////////////////
//
// Register/wire signal declarations
//
////////////////////////////////////////////////////////////////////////
//
// {{{
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;
//
//
wire tx_busy;
//
reg [30:0] uart_setup;
//
wire rx_stb, rx_break, rx_perr, rx_ferr, ck_uart;
wire [7:0] rx_uart_data;
reg rx_uart_reset;
//
wire rx_empty_n, rx_fifo_err;
wire [7:0] rxf_axil_data;
wire [15:0] rxf_status;
reg rxf_axil_read;
reg r_rx_perr, r_rx_ferr;
//
wire [(LCLLGFLEN-1):0] check_cutoff;
wire [31:0] axil_rx_data;
//
wire tx_empty_n, txf_err, tx_break;
wire [7:0] tx_data;
wire [15:0] txf_status;
reg txf_axil_write, tx_uart_reset;
reg [7:0] txf_axil_data;
wire [31:0] axil_tx_data;
wire [31:0] axil_fifo_data;
//
reg [1:0] r_axil_addr;
reg r_preread;
reg [31:0] new_setup;
// }}}
////////////////////////////////////////////////////////////////////////
//
// 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));
// High bandwidth reads
assign axil_read_ready = arskd_valid
&& (!r_preread || !axil_read_valid
|| S_AXI_RREADY);
end else begin : SIMPLE_READS
reg axil_arready;
initial axil_arready = 1;
always @(posedge S_AXI_ACLK)
if (!S_AXI_ARESETN)
axil_arready <= 1;
else if (S_AXI_ARVALID && S_AXI_ARREADY)
axil_arready <= 0;
else if (S_AXI_RVALID && S_AXI_RREADY)
axil_arready <= 1;
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 (r_preread)
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
//
////////////////////////////////////////////////////////////////////////
//
// {{{
localparam [1:0] UART_SETUP = 2'b00,
UART_FIFO = 2'b01,
UART_RXREG = 2'b10,
UART_TXREG = 2'b11;
always @(*)
new_setup = apply_wstrb({1'b0,uart_setup},wskd_data,wskd_strb);
//
// The UART setup parameters: bits per byte, stop bits, parity, and
// baud rate are all captured within this uart_setup register.
//
initial uart_setup = INITIAL_SETUP
| ((HARDWARE_FLOW_CONTROL_PRESENT==1'b0)? 31'h40000000 : 0);
always @(posedge S_AXI_ACLK)
if ((axil_write_ready)&&(awskd_addr == UART_SETUP))
begin
uart_setup <= new_setup[30:0];
if (!HARDWARE_FLOW_CONTROL_PRESENT)
uart_setup[30] <= 1'b1;
end
/////////////////////////////////////////
//
// First, the UART receiver
// {{{
/////////////////////////////////////////
//
//
// Here's our UART receiver. Basically, it accepts our setup wires,
// the UART input, a clock, and a reset line, and produces outputs:
// a stb (true when new data is ready), and an 8-bit data out value
// valid when stb is high.
`ifdef FORMAL
(* anyseq *) reg w_rx_break, w_rx_perr, w_rx_ferr, w_ck_uart;
assign rx_break = w_rx_break;
assign w_rx_perr = w_rx_perr;
assign w_rx_ferr = w_rx_ferr;
assign ck_uart = w_ck_uart;
`else
`ifdef USE_LITE_UART
rxuartlite #(.CLOCKS_PER_BAUD(INITIAL_SETUP[23:0]))
rx(S_AXI_ACLK, i_uart_rx, rx_stb, rx_uart_data);
assign rx_break = 1'b0;
assign rx_perr = 1'b0;
assign rx_ferr = 1'b0;
assign ck_uart = 1'b0;
`else
// The full receiver also produces a break value (true during a break
// cond.), and parity/framing error flags--also valid when stb is true.
rxuart #(.INITIAL_SETUP(INITIAL_SETUP)) rx(S_AXI_ACLK, (!S_AXI_ARESETN)||(rx_uart_reset),
uart_setup, i_uart_rx,
rx_stb, rx_uart_data, rx_break,
rx_perr, rx_ferr, ck_uart);
// The real trick is ... now that we have this extra data, what do we do
// with it?
`endif
`endif // FORMAL
// We place it into a receiver FIFO.
//
// Note that the FIFO will be cleared upon any reset: either if there's
// a UART break condition on the line, the receiver is in reset, or an
// external reset is issued.
//
// The FIFO accepts strobe and data from the receiver.
// We issue another wire to it (rxf_axil_read), true when we wish to
// read from the FIFO, and we get our data in rxf_axil_data. The FIFO
// outputs four status-type values: 1) is it non-empty, 2) is the FIFO
// over half full, 3) a 16-bit status register, containing info
// regarding how full the FIFO truly is, and 4) an error indicator.
ufifo #(.LGFLEN(LCLLGFLEN), .RXFIFO(1))
rxfifo(S_AXI_ACLK, (!S_AXI_ARESETN)||(rx_break)||(rx_uart_reset),
rx_stb, rx_uart_data,
rx_empty_n,
rxf_axil_read, rxf_axil_data,
rxf_status, rx_fifo_err);
assign o_uart_rxfifo_int = rxf_status[1];
// We produce four interrupts. One of the receive interrupts indicates
// whether or not the receive FIFO is non-empty. This should wake up
// the CPU.
assign o_uart_rx_int = rxf_status[0];
// The clear to send line, which may be ignored, but which we set here
// to be true any time the FIFO has fewer than N-2 items in it.
// Why not N-1? Because at N-1 we are totally full, but already so full
// that if the transmit end starts sending we won't have a location to
// receive it. (Transmit might've started on the next character by the
// time we set this--thus we need to set it to one, one character before
// necessary).
assign check_cutoff = -3;
always @(posedge S_AXI_ACLK)
o_rts_n <= ((HARDWARE_FLOW_CONTROL_PRESENT)
&&(!uart_setup[30])
&&(rxf_status[(LCLLGFLEN+1):2] > check_cutoff));
// If the bus requests that we read from the receive FIFO, we need to
// tell this to the receive FIFO. Note that because we are using a
// clock here, the output from the receive FIFO will necessarily be
// delayed by an extra clock.
initial rxf_axil_read = 1'b0;
always @(posedge S_AXI_ACLK)
rxf_axil_read<=(axil_read_ready)&&(arskd_addr[1:0]==UART_RXREG);
// Now, let's deal with those RX UART errors: both the parity and frame
// errors. As you may recall, these are valid only when rx_stb is
// valid, so we need to hold on to them until the user reads them via
// a UART read request..
initial r_rx_perr = 1'b0;
initial r_rx_ferr = 1'b0;
always @(posedge S_AXI_ACLK)
if ((rx_uart_reset)||(rx_break))
begin
// Clear the error
r_rx_perr <= 1'b0;
r_rx_ferr <= 1'b0;
end else if (axil_write_ready&&awskd_addr == UART_RXREG && wskd_strb[1])
begin
// Reset the error lines if a '1' is ever written to
// them, otherwise leave them alone.
//
r_rx_perr <= (r_rx_perr)&&(!wskd_data[9]);
r_rx_ferr <= (r_rx_ferr)&&(!wskd_data[10]);
end else if (rx_stb)
begin
// On an rx_stb, capture any parity or framing error
// indications. These aren't kept with the data rcvd,
// but rather kept external to the FIFO. As a result,
// if you get a parity or framing error, you will never
// know which data byte it was associated with.
// For now ... that'll work.
r_rx_perr <= (r_rx_perr)||(rx_perr);
r_rx_ferr <= (r_rx_ferr)||(rx_ferr);
end
initial rx_uart_reset = 1'b1;
always @(posedge S_AXI_ACLK)
if ((!S_AXI_ARESETN)||((axil_write_ready)&&(awskd_addr[1:0]== UART_SETUP) && (&wskd_strb)))
// The receiver reset, always set on a master reset
// request.
rx_uart_reset <= 1'b1;
else if (axil_write_ready&&(awskd_addr[1:0]==UART_RXREG)&&wskd_strb[1])
// Writes to the receive register will command a receive
// reset anytime bit[12] is set.
rx_uart_reset <= wskd_data[12];
else
rx_uart_reset <= 1'b0;
// Finally, we'll construct a 32-bit value from these various wires,
// to be returned over the bus on any read. These include the data
// that would be read from the FIFO, an error indicator set upon
// reading from an empty FIFO, a break indicator, and the frame and
// parity error signals.
assign axil_rx_data = { 16'h00,
3'h0, rx_fifo_err,
rx_break, rx_ferr, r_rx_perr, !rx_empty_n,
rxf_axil_data};
// }}}
/////////////////////////////////////////
//
// Then the UART transmitter
// {{{
/////////////////////////////////////////
//
// Unlike the receiver which goes from RXUART -> UFIFO -> WB, the
// transmitter basically goes WB -> UFIFO -> TXUART. Hence, to build
// support for the transmitter, we start with the command to write data
// into the FIFO. In this case, we use the act of writing to the
// UART_TXREG address as our indication that we wish to write to the
// FIFO. Here, we create a write command line, and latch the data for
// the extra clock that it'll take so that the command and data can be
// both true on the same clock.
initial txf_axil_write = 1'b0;
always @(posedge S_AXI_ACLK)
begin
txf_axil_write <= (axil_write_ready)&&(awskd_addr == UART_TXREG)
&& wskd_strb[0];
txf_axil_data <= wskd_data[7:0];
end
// Transmit FIFO
//
// Most of this is just wire management. The TX FIFO is identical in
// implementation to the RX FIFO (theyre both UFIFOs), but the TX
// FIFO is fed from the WB and read by the transmitter. Some key
// differences to note: we reset the transmitter on any request for a
// break. We read from the FIFO any time the UART transmitter is idle.
// and ... we just set the values (above) for controlling writing into
// this.
ufifo #(.LGFLEN(LGFLEN), .RXFIFO(0))
txfifo(S_AXI_ACLK, (tx_break)||(tx_uart_reset),
txf_axil_write, txf_axil_data,
tx_empty_n,
(!tx_busy)&&(tx_empty_n), tx_data,
txf_status, txf_err);
// Let's create two transmit based interrupts from the FIFO for the CPU.
// The first will be true any time the FIFO has at least one open
// position within it.
assign o_uart_tx_int = txf_status[0];
// The second will be true any time the FIFO is less than half
// full, allowing us a change to always keep it (near) fully
// charged.
assign o_uart_txfifo_int = txf_status[1];
`ifndef USE_LITE_UART
// Break logic
//
// A break in a UART controller is any time the UART holds the line
// low for an extended period of time. Here, we capture the
// wskd_data[9] wire, on writes, as an indication we wish to break.
// As long as you write unsigned characters to the interface, this
// will never be true unless you wish it to be true. Be aware, though,
// writing a valid value to the interface will bring it out of the
// break condition.
reg r_tx_break;
initial r_tx_break = 1'b0;
always @(posedge S_AXI_ACLK)
if (!S_AXI_ARESETN)
r_tx_break <= 1'b0;
else if (axil_write_ready &&(awskd_addr[1:0]== UART_TXREG) &&
wskd_strb[1])
r_tx_break <= wskd_data[9];
assign tx_break = r_tx_break;
`else
assign tx_break = 1'b0;
`endif
// TX-Reset logic
//
// This is nearly identical to the RX reset logic above. Basically,
// any time someone writes to bit [12] the transmitter will go through
// a reset cycle. Keep bit [12] low, and everything will proceed as
// normal.
initial tx_uart_reset = 1'b1;
always @(posedge S_AXI_ACLK)
if ((!S_AXI_ARESETN)||((axil_write_ready)&&(awskd_addr == UART_SETUP)))
tx_uart_reset <= 1'b1;
else if ((axil_write_ready)&&(awskd_addr[1:0]== UART_TXREG) && wskd_strb[1])
tx_uart_reset <= wskd_data[12];
else
tx_uart_reset <= 1'b0;
`ifdef FORMAL
(* anyseq *) reg w_uart_tx, w_tx_busy;
assign tx_busy = w_uart_tx;
assign o_uart_tx = w_uart_tx;
`else
`ifdef USE_LITE_UART
txuartlite #(.CLOCKS_PER_BAUD(INITIAL_SETUP[23:0])) tx(S_AXI_ACLK, (tx_empty_n), tx_data,
o_uart_tx, tx_busy);
`else
wire cts_n;
assign cts_n = (HARDWARE_FLOW_CONTROL_PRESENT)&&(i_cts_n);
// Finally, the UART transmitter module itself. Note that we haven't
// connected the reset wire. Transmitting is as simple as setting
// the stb value (here set to tx_empty_n) and the data. When these
// are both set on the same clock that tx_busy is low, the transmitter
// will move on to the next data byte. Really, the only thing magical
// here is that tx_empty_n wire--thus, if there's anything in the FIFO,
// we read it here. (You might notice above, we register a read any
// time (tx_empty_n) and (!tx_busy) are both true---the condition for
// starting to transmit a new byte.)
txuart #(.INITIAL_SETUP(INITIAL_SETUP)) tx(S_AXI_ACLK, 1'b0, uart_setup,
r_tx_break, (tx_empty_n), tx_data,
cts_n, o_uart_tx, tx_busy);
`endif
`endif // FORMAL
// Now that we are done with the chain, pick some wires for the user
// to read on any read of the transmit port.
//
// This port is different from reading from the receive port, since
// there are no side effects. (Reading from the receive port advances
// the receive FIFO, here only writing to the transmit port advances the
// transmit FIFO--hence the read values are free for ... whatever.)
// We choose here to provide information about the transmit FIFO
// (txf_err, txf_half_full, txf_full_n), information about the current
// voltage on the line (o_uart_tx)--and even the voltage on the receive
// line (ck_uart), as well as our current setting of the break and
// whether or not we are actively transmitting.
assign axil_tx_data = { 16'h00,
i_cts_n, txf_status[1:0], txf_err,
ck_uart, o_uart_tx, tx_break, (tx_busy|txf_status[0]),
(tx_busy|txf_status[0])?txf_axil_data:8'b00};
// }}}
/////////////////////////////////////////
//
// FIFO return
// {{{
/////////////////////////////////////////
//
// Each of the FIFO's returns a 16 bit status value. This value tells
// us both how big the FIFO is, as well as how much of the FIFO is in
// use. Let's merge those two status words together into a word we
// can use when reading about the FIFO.
assign axil_fifo_data = { txf_status, rxf_status };
// }}}
/////////////////////////////////////////
//
// Final read register
// {{{
/////////////////////////////////////////
//
// You may recall from above that reads take two clocks. Hence, we
// need to delay the address decoding for a clock until the data is
// ready. We do that here.
initial r_preread = 0;
always @(posedge S_AXI_ACLK)
if (!S_AXI_ARESETN)
r_preread <= 0;
else if (axil_read_ready)
r_preread <= 1;
else if (!S_AXI_RVALID || S_AXI_RREADY)
r_preread <= 0;
always @(posedge S_AXI_ACLK)
if (axil_read_ready)
r_axil_addr <= arskd_addr;
// Finally, set the return data. This data must be valid on the same
// clock S_AXI_RVALID is high. On all other clocks, it is
// irrelelant--since no one cares, no one is reading it, it gets lost
// in the mux in the interconnect, etc. For this reason, we can just
// simplify our logic.
always @(posedge S_AXI_ACLK)
if (!S_AXI_RVALID || S_AXI_RREADY)
begin
casez(r_axil_addr)
UART_SETUP: axil_read_data <= { 1'b0, uart_setup };
UART_FIFO: axil_read_data <= axil_fifo_data;
UART_RXREG: axil_read_data <= axil_rx_data;
UART_TXREG: axil_read_data <= axil_tx_data;
endcase
if (OPT_LOWPOWER && !r_preread)
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
// }}}
////////////////////////////////////////////////////////////////////////
//
// Veri1ator lint-check
// {{{
// 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], new_setup[31] };
// Verilator lint_on UNUSED
// }}}
`ifdef FORMAL
////////////////////////////////////////////////////////////////////////
//
// Formal properties used in verfiying this core
//
////////////////////////////////////////////////////////////////////////
//
// {{{
reg f_past_valid;
initial f_past_valid = 0;
always @(posedge S_AXI_ACLK)
f_past_valid <= 1;
////////////////////////////////////////////////////////////////////////
//
// 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(4),
.F_AXI_MAXDELAY(4),
.F_AXI_MAXRSTALL(3),
.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_awcache(4'h0),
.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_arcache(4'h0),
.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)
+ (r_preread ? 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)
+ (r_preread ? 1:0));
assert(S_AXI_ARREADY == (!S_AXI_RVALID && !r_preread));
end
`ifdef VERIFIC
assert property (@(posedge S_AXI_ACLK)
disable iff (!S_AXI_ARESETN || (S_AXI_RVALID && !S_AXI_RREADY))
S_AXI_ARVALID && S_AXI_ARREADY && S_AXI_ARADDR[3:2]== UART_SETUP
|=> r_preread && r_axil_addr == UART_SETUP
##1 S_AXI_RVALID && axil_read_data
== { 1'b0, $past(uart_setup) });
assert property (@(posedge S_AXI_ACLK)
disable iff (!S_AXI_ARESETN || (S_AXI_RVALID && !S_AXI_RREADY))
S_AXI_ARVALID && S_AXI_ARREADY && S_AXI_ARADDR[3:2] == UART_FIFO
|=> r_preread && r_axil_addr == UART_FIFO
##1 S_AXI_RVALID && axil_read_data == $past(axil_fifo_data));
assert property (@(posedge S_AXI_ACLK)
disable iff (!S_AXI_ARESETN || (S_AXI_RVALID && !S_AXI_RREADY))
S_AXI_ARVALID && S_AXI_ARREADY && S_AXI_ARADDR[3:2]== UART_RXREG
|=> r_preread && r_axil_addr == UART_RXREG
##1 S_AXI_RVALID && axil_read_data == $past(axil_rx_data));
assert property (@(posedge S_AXI_ACLK)
disable iff (!S_AXI_ARESETN || (S_AXI_RVALID && !S_AXI_RREADY))
S_AXI_ARVALID && S_AXI_ARREADY && S_AXI_ARADDR[3:2]== UART_TXREG
|=> r_preread && r_axil_addr == UART_TXREG
##1 S_AXI_RVALID && axil_read_data == $past(axil_tx_data));
`endif
//
// 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);
// }}}
////////////////////////////////////////////////////////////////////////
//
// Cover checks
//
////////////////////////////////////////////////////////////////////////
//
// {{{
// While there are already cover properties in the formal property
// set above, you'll probably still want to cover something
// application specific here
// }}}
// }}}
`endif
endmodule