blob: d81fb609bca9c239e311c9e8e010e1b7c2b13153 [file] [log] [blame]
////////////////////////////////////////////////////////////////////////////////
//
// Filename: axilrd2wbsp.v (AXI lite to wishbone slave, read channel)
//
// Project: WB2AXIPSP: bus bridges and other odds and ends
//
// Purpose: Bridge an AXI lite read channel pair to a single wishbone read
// channel.
//
// Creator: Dan Gisselquist, Ph.D.
// Gisselquist Technology, LLC
//
////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2016-2020, 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 axilrd2wbsp(i_clk, i_axi_reset_n,
// AXI read address channel signals
o_axi_arready, i_axi_araddr, i_axi_arcache, i_axi_arprot, i_axi_arvalid,
// AXI read data channel signals
o_axi_rresp, o_axi_rvalid, o_axi_rdata, i_axi_rready,
// We'll share the clock and the reset
o_wb_cyc, o_wb_stb, o_wb_addr,
i_wb_ack, i_wb_stall, i_wb_data, i_wb_err
`ifdef FORMAL
, f_first, f_mid, f_last
`endif
);
localparam C_AXI_DATA_WIDTH = 32;// Width of the AXI R&W data
parameter C_AXI_ADDR_WIDTH = 28; // AXI Address width
localparam AW = C_AXI_ADDR_WIDTH-2;// WB Address width
parameter LGFIFO = 3;
input wire i_clk; // Bus clock
input wire i_axi_reset_n; // Bus reset
// AXI read address channel signals
output reg o_axi_arready; // Read address ready
input wire [C_AXI_ADDR_WIDTH-1:0] i_axi_araddr; // Read address
input wire [3:0] i_axi_arcache; // Read Cache type
input wire [2:0] i_axi_arprot; // Read Protection type
input wire i_axi_arvalid; // Read address valid
// AXI read data channel signals
output reg [1:0] o_axi_rresp; // Read response
output reg o_axi_rvalid; // Read reponse valid
output wire [C_AXI_DATA_WIDTH-1:0] o_axi_rdata; // Read data
input wire i_axi_rready; // Read Response ready
// We'll share the clock and the reset
output reg o_wb_cyc;
output reg o_wb_stb;
output reg [(AW-1):0] o_wb_addr;
input wire i_wb_ack;
input wire i_wb_stall;
input wire [(C_AXI_DATA_WIDTH-1):0] i_wb_data;
input wire i_wb_err;
`ifdef FORMAL
// Output connections only used in formal mode
output wire [LGFIFO:0] f_first;
output wire [LGFIFO:0] f_mid;
output wire [LGFIFO:0] f_last;
`endif
localparam DW = C_AXI_DATA_WIDTH;
localparam AXI_LSBS = $clog2(C_AXI_DATA_WIDTH)-3;
wire w_reset;
assign w_reset = (!i_axi_reset_n);
reg r_stb;
reg [AW-1:0] r_addr;
localparam FLEN=(1<<LGFIFO);
reg [DW-1:0] dfifo [0:(FLEN-1)];
reg fifo_full, fifo_empty;
reg [LGFIFO:0] r_first, r_mid, r_last;
wire [LGFIFO:0] next_first, next_last;
reg wb_pending;
reg [LGFIFO:0] wb_outstanding;
wire [DW-1:0] read_data;
reg err_state;
reg [LGFIFO:0] err_loc;
initial o_wb_cyc = 1'b0;
initial o_wb_stb = 1'b0;
always @(posedge i_clk)
if ((w_reset)||((o_wb_cyc)&&(i_wb_err))||(err_state))
o_wb_stb <= 1'b0;
else if (r_stb || ((i_axi_arvalid)&&(o_axi_arready)))
o_wb_stb <= 1'b1;
else if ((o_wb_cyc)&&(!i_wb_stall))
o_wb_stb <= 1'b0;
always @(*)
o_wb_cyc = (wb_pending)||(o_wb_stb);
always @(posedge i_clk)
if (r_stb && !i_wb_stall)
o_wb_addr <= r_addr;
else if ((o_axi_arready)&&((!o_wb_stb)||(!i_wb_stall)))
o_wb_addr <= i_axi_araddr[AW+1:AXI_LSBS];
// Shadow request
// r_stb, r_addr
initial r_stb = 1'b0;
always @(posedge i_clk)
begin
if ((i_axi_arvalid)&&(o_axi_arready)&&(o_wb_stb)&&(i_wb_stall))
begin
r_stb <= 1'b1;
r_addr <= i_axi_araddr[AW+1:AXI_LSBS];
end else if ((!i_wb_stall)||(!o_wb_cyc))
r_stb <= 1'b0;
if ((w_reset)||(o_wb_cyc)&&(i_wb_err)||(err_state))
r_stb <= 1'b0;
end
initial wb_pending = 0;
initial wb_outstanding = 0;
always @(posedge i_clk)
if ((w_reset)||(!o_wb_cyc)||(i_wb_err)||(err_state))
begin
wb_pending <= 1'b0;
wb_outstanding <= 0;
end else case({ (o_wb_stb)&&(!i_wb_stall), i_wb_ack })
2'b01: begin
wb_outstanding <= wb_outstanding - 1'b1;
wb_pending <= (wb_outstanding >= 2);
end
2'b10: begin
wb_outstanding <= wb_outstanding + 1'b1;
wb_pending <= 1'b1;
end
default: begin end
endcase
assign next_first = r_first + 1'b1;
assign next_last = r_last + 1'b1;
initial fifo_full = 1'b0;
initial fifo_empty = 1'b1;
always @(posedge i_clk)
if (w_reset)
begin
fifo_full <= 1'b0;
fifo_empty <= 1'b1;
end else case({ (o_axi_rvalid)&&(i_axi_rready),
(i_axi_arvalid)&&(o_axi_arready) })
2'b01: begin
fifo_full <= (next_first[LGFIFO-1:0] == r_last[LGFIFO-1:0])
&&(next_first[LGFIFO]!=r_last[LGFIFO]);
fifo_empty <= 1'b0;
end
2'b10: begin
fifo_full <= 1'b0;
fifo_empty <= 1'b0;
end
default: begin end
endcase
initial o_axi_arready = 1'b1;
always @(posedge i_clk)
if (w_reset)
o_axi_arready <= 1'b1;
else if ((o_wb_cyc && i_wb_err) || err_state)
// On any error, drop the ready flag until it's been flushed
o_axi_arready <= 1'b0;
else if ((i_axi_arvalid)&&(o_axi_arready)&&(o_wb_stb)&&(i_wb_stall))
// On any request where we are already busy, r_stb will get
// set and we drop arready
o_axi_arready <= 1'b0;
else if (!o_axi_arready && o_wb_stb && i_wb_stall)
// If we've already stalled on o_wb_stb, remain stalled until
// the bus clears
o_axi_arready <= 1'b0;
else if (fifo_full && (!o_axi_rvalid || !i_axi_rready))
// If the FIFO is full, we must remain not ready until at
// least one acknowledgment is accepted
o_axi_arready <= 1'b0;
else if ( (!o_axi_rvalid || !i_axi_rready)
&& (i_axi_arvalid && o_axi_arready))
o_axi_arready <= (next_first[LGFIFO-1:0] != r_last[LGFIFO-1:0])
||(next_first[LGFIFO]==r_last[LGFIFO]);
else
o_axi_arready <= 1'b1;
initial r_first = 0;
always @(posedge i_clk)
if (w_reset)
r_first <= 0;
else if ((i_axi_arvalid)&&(o_axi_arready))
r_first <= r_first + 1'b1;
initial r_mid = 0;
always @(posedge i_clk)
if (w_reset)
r_mid <= 0;
else if ((o_wb_cyc)&&((i_wb_ack)||(i_wb_err)))
r_mid <= r_mid + 1'b1;
else if ((err_state)&&(r_mid != r_first))
r_mid <= r_mid + 1'b1;
initial r_last = 0;
always @(posedge i_clk)
if (w_reset)
r_last <= 0;
else if ((o_axi_rvalid)&&(i_axi_rready))
r_last <= r_last + 1'b1;
always @(posedge i_clk)
if ((o_wb_cyc)&&((i_wb_ack)||(i_wb_err)))
dfifo[r_mid[(LGFIFO-1):0]] <= i_wb_data;
always @(posedge i_clk)
if ((o_wb_cyc)&&(i_wb_err))
err_loc <= r_mid;
assign read_data = dfifo[r_last[LGFIFO-1:0]];
assign o_axi_rdata = read_data[DW-1:0];
initial o_axi_rresp = 2'b00;
always @(posedge i_clk)
if (w_reset)
o_axi_rresp <= 0;
else if ((!o_axi_rvalid)||(i_axi_rready))
begin
if ((!err_state)&&((!o_wb_cyc)||(!i_wb_err)))
o_axi_rresp <= 2'b00;
else if ((!err_state)&&(o_wb_cyc)&&(i_wb_err))
begin
if (o_axi_rvalid)
o_axi_rresp <= (r_mid == next_last) ? 2'b10 : 2'b00;
else
o_axi_rresp <= (r_mid == r_last) ? 2'b10 : 2'b00;
end else if (err_state)
begin
if (next_last == err_loc)
o_axi_rresp <= 2'b10;
else if (o_axi_rresp[1])
o_axi_rresp <= 2'b11;
end else
o_axi_rresp <= 0;
end
initial err_state = 0;
always @(posedge i_clk)
if (w_reset)
err_state <= 0;
else if (r_first == r_last)
err_state <= 0;
else if ((o_wb_cyc)&&(i_wb_err))
err_state <= 1'b1;
initial o_axi_rvalid = 1'b0;
always @(posedge i_clk)
if (w_reset)
o_axi_rvalid <= 0;
else if ((o_wb_cyc)&&((i_wb_ack)||(i_wb_err)))
o_axi_rvalid <= 1'b1;
else if ((o_axi_rvalid)&&(i_axi_rready))
begin
if (err_state)
o_axi_rvalid <= (next_last != r_first);
else
o_axi_rvalid <= (next_last != r_mid);
end
// Make Verilator happy
// verilator lint_off UNUSED
wire unused;
assign unused = &{ 1'b0, i_axi_arcache, i_axi_arprot,
i_axi_araddr[AXI_LSBS-1:0], fifo_empty };
// verilator lint_on UNUSED
`ifdef FORMAL
reg f_past_valid;
reg f_fifo_empty;
reg [LGFIFO:0] f_fifo_fill;
initial f_past_valid = 1'b0;
always @(posedge i_clk)
f_past_valid <= 1'b1;
always @(*)
begin
f_fifo_fill = (r_first - r_last);
f_fifo_empty = (f_fifo_fill == 0);
end
always @(*)
if (!f_past_valid)
assume(w_reset);
always @(*)
if (err_state)
assert(!o_axi_arready);
always @(*)
if (err_state)
assert((!o_wb_cyc)&&(!o_axi_arready));
always @(*)
if ((f_fifo_empty)&&(!w_reset))
assert((!fifo_full)&&(r_first == r_last)&&(r_mid == r_last));
always @(*)
if (fifo_full)
assert((!f_fifo_empty)
&&(r_first[LGFIFO-1:0] == r_last[LGFIFO-1:0])
&&(r_first[LGFIFO] != r_last[LGFIFO]));
always @(*)
assert(f_fifo_fill <= (1<<LGFIFO));
always @(*)
if (fifo_full)
assert(!o_axi_arready);
always @(*)
assert(fifo_full == (f_fifo_fill == (1<<LGFIFO)));
always @(*)
if (f_fifo_fill == (1<<LGFIFO))
assert(!o_axi_arready);
always @(*)
assert(wb_pending == (wb_outstanding != 0));
assign f_first = r_first;
assign f_mid = r_mid;
assign f_last = r_last;
wire [LGFIFO:0] f_wb_nreqs, f_wb_nacks, f_wb_outstanding;
fwb_master #(
.AW(AW), .DW(DW), .F_LGDEPTH(LGFIFO+1)
) fwb(i_clk, w_reset,
o_wb_cyc, o_wb_stb, 1'b0, o_wb_addr, 32'h0, 4'h0,
i_wb_ack, i_wb_stall, i_wb_data, i_wb_err,
f_wb_nreqs,f_wb_nacks, f_wb_outstanding);
always @(*)
if (o_wb_cyc)
assert(f_wb_outstanding == wb_outstanding);
always @(*)
if (o_wb_cyc)
assert(wb_outstanding <= (1<<LGFIFO));
wire [LGFIFO:0] wb_fill;
assign wb_fill = r_first - r_mid;
always @(*)
assert(wb_fill <= f_fifo_fill);
always @(*)
if (o_wb_stb)
assert(wb_outstanding+1+((r_stb)?1:0) == wb_fill);
else if (o_wb_cyc)
assert(wb_outstanding == wb_fill);
always @(*)
if (r_stb)
begin
assert(o_wb_stb);
assert(!o_axi_arready);
end
wire [LGFIFO:0] f_axi_rd_outstanding,
f_axi_wr_outstanding,
f_axi_awr_outstanding;
faxil_slave #(
.C_AXI_ADDR_WIDTH(C_AXI_ADDR_WIDTH),
.F_LGDEPTH(LGFIFO+1),
.F_OPT_READ_ONLY(1'b1),
.F_AXI_MAXWAIT(0),
.F_AXI_MAXDELAY(0)
) faxil(i_clk, i_axi_reset_n,
//
// AXI write address channel signals
1'b0, i_axi_araddr, i_axi_arcache, i_axi_arprot, 1'b0,
// AXI write data channel signals
1'b0, 32'h0, 4'h0, 1'b0,
// AXI write response channel signals
2'b00, 1'b0, 1'b0,
// AXI read address channel signals
o_axi_arready, i_axi_araddr, i_axi_arcache, i_axi_arprot,
i_axi_arvalid,
// AXI read data channel signals
o_axi_rresp, o_axi_rvalid, o_axi_rdata, i_axi_rready,
f_axi_rd_outstanding, f_axi_wr_outstanding,
f_axi_awr_outstanding);
always @(*)
assert(f_axi_wr_outstanding == 0);
always @(*)
assert(f_axi_awr_outstanding == 0);
always @(*)
assert(f_axi_rd_outstanding == f_fifo_fill);
wire [LGFIFO:0] f_mid_minus_err, f_err_minus_last,
f_first_minus_err;
assign f_mid_minus_err = f_mid - err_loc;
assign f_err_minus_last = err_loc - f_last;
assign f_first_minus_err = f_first - err_loc;
always @(*)
if (o_axi_rvalid)
begin
if (!err_state)
assert(!o_axi_rresp[1]);
else if (err_loc == f_last)
assert(o_axi_rresp == 2'b10);
else if (f_err_minus_last < (1<<LGFIFO))
assert(!o_axi_rresp[1]);
else
assert(o_axi_rresp[1]);
end
always @(*)
if (err_state)
assert(o_axi_rvalid == (r_first != r_last));
else
assert(o_axi_rvalid == (r_mid != r_last));
always @(*)
if (err_state)
assert(f_first_minus_err <= (1<<LGFIFO));
always @(*)
if (err_state)
assert(f_first_minus_err != 0);
always @(*)
if (err_state)
assert(f_mid_minus_err <= f_first_minus_err);
always @(*)
if ((f_past_valid)&&(i_axi_reset_n)&&(f_axi_rd_outstanding > 0))
begin
if (err_state)
assert((!o_wb_cyc)&&(f_wb_outstanding == 0));
else if (!o_wb_cyc)
assert((o_axi_rvalid)&&(f_axi_rd_outstanding>0)
&&(wb_fill == 0));
end
// WB covers
always @(*)
cover(o_wb_cyc && o_wb_stb);
always @(*)
if (LGFIFO > 2)
cover(o_wb_cyc && f_wb_outstanding > 2);
always @(posedge i_clk)
cover(o_wb_cyc && i_wb_ack
&& $past(o_wb_cyc && i_wb_ack)
&& $past(o_wb_cyc && i_wb_ack,2));
// AXI covers
always @(*)
cover(o_axi_rvalid && i_axi_rready);
always @(posedge i_clk)
cover(i_axi_arvalid && o_axi_arready
&& $past(i_axi_arvalid && o_axi_arready)
&& $past(i_axi_arvalid && o_axi_arready,2));
always @(posedge i_clk)
cover(o_axi_rvalid && i_axi_rready
&& $past(o_axi_rvalid && i_axi_rready)
&& $past(o_axi_rvalid && i_axi_rready,2));
`endif
endmodule
`ifndef YOSYS
`default_nettype wire
`endif