blob: a42e80f63d8ff3360d8bac6caa7eb4ae8de05385 [file] [log] [blame]
////////////////////////////////////////////////////////////////////////////////
//
// Filename: aximrd2wbsp.v
//
// Project: WB2AXIPSP: bus bridges and other odds and ends
//
// Purpose: Bridge an AXI read channel pair to a single wishbone read
// channel.
//
// Rules:
// 1. Any read channel error *must* be returned to the correct
// read channel ID. In other words, we can't pipeline between IDs
// 2. A FIFO must be used on getting a WB return, to make certain that
// the AXI return channel is able to stall with no loss
// 3. No request can be accepted unless there is room in the return
// channel for it
//
// Status: Passes a formal bounded model check at 15 steps. Should be
// ready for a hardware check.
//
//
// Creator: Dan Gisselquist, Ph.D.
// Gisselquist Technology, LLC
//
////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2019-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 aximrd2wbsp #(
parameter C_AXI_ID_WIDTH = 6, // The AXI id width used for R&W
// This is an int between 1-16
parameter C_AXI_DATA_WIDTH = 32,// Width of the AXI R&W data
parameter C_AXI_ADDR_WIDTH = 28, // AXI Address width
parameter AW = 26, // AXI Address width
parameter LGFIFO = 3,
parameter [0:0] OPT_SWAP_ENDIANNESS = 0
// parameter WBMODE = "B4PIPELINE"
// Could also be "BLOCK"
) (
input wire S_AXI_ACLK, // Bus clock
input wire S_AXI_ARESETN, // Bus reset
// AXI read address channel signals
input wire S_AXI_ARVALID, // Read address valid
output wire S_AXI_ARREADY, // Read address ready
input wire [C_AXI_ID_WIDTH-1:0] S_AXI_ARID, // Read ID
input wire [C_AXI_ADDR_WIDTH-1:0] S_AXI_ARADDR, // Read address
input wire [7:0] S_AXI_ARLEN, // Read Burst Length
input wire [2:0] S_AXI_ARSIZE, // Read Burst size
input wire [1:0] S_AXI_ARBURST, // Read Burst type
input wire [0:0] S_AXI_ARLOCK, // Read lock type
input wire [3:0] S_AXI_ARCACHE, // Read Cache type
input wire [2:0] S_AXI_ARPROT, // Read Protection type
input wire [3:0] S_AXI_ARQOS, // Read Protection type
// AXI read data channel signals
output reg S_AXI_RVALID, // Read reponse valid
input wire S_AXI_RREADY, // Read Response ready
output wire [C_AXI_ID_WIDTH-1:0] S_AXI_RID, // Response ID
output wire [C_AXI_DATA_WIDTH-1:0] S_AXI_RDATA, // Read data
output wire S_AXI_RLAST, // Read last
output reg [1:0] S_AXI_RRESP, // Read response
// 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_stall,
input wire i_wb_ack,
input wire [(C_AXI_DATA_WIDTH-1):0] i_wb_data,
input wire i_wb_err
);
localparam DW = C_AXI_DATA_WIDTH;
wire w_reset;
assign w_reset = (S_AXI_ARESETN == 1'b0);
wire lastid_fifo_full, lastid_fifo_empty,
resp_fifo_full, resp_fifo_empty;
wire [LGFIFO:0] lastid_fifo_fill, resp_fifo_fill;
reg last_stb, last_ack, err_state;
reg [C_AXI_ID_WIDTH-1:0] axi_id;
reg [7:0] stblen;
wire skid_arvalid;
wire [C_AXI_ID_WIDTH-1:0] skid_arid;// r_id;
wire [C_AXI_ADDR_WIDTH-1:0] skid_araddr;// r_addr;
wire [7:0] skid_arlen;// r_len;
wire [2:0] skid_arsize;// r_size;
wire [1:0] skid_arburst;// r_burst;
reg fifo_nearfull;
wire accept_request;
reg [1:0] axi_burst;
reg [2:0] axi_size;
reg [7:0] axi_len;
reg [C_AXI_ADDR_WIDTH-1:0] axi_addr;
wire [C_AXI_ADDR_WIDTH-1:0] next_addr;
wire response_err;
wire lastid_fifo_wr;
reg midissue, wb_empty;
reg [LGFIFO+7:0] acks_expected;
skidbuffer #(
.OPT_OUTREG(0),
.DW(C_AXI_ID_WIDTH + C_AXI_ADDR_WIDTH + 8 + 3 + 2)
) axirqbuf(S_AXI_ACLK, !S_AXI_ARESETN,
S_AXI_ARVALID, S_AXI_ARREADY,
{ S_AXI_ARID, S_AXI_ARADDR, S_AXI_ARLEN,
S_AXI_ARSIZE, S_AXI_ARBURST },
skid_arvalid, accept_request,
{ skid_arid, skid_araddr, skid_arlen,
skid_arsize, skid_arburst });
assign accept_request = (!err_state)
&&((!o_wb_cyc)||(!i_wb_err))
// &&(!lastid_fifo_full)
&&(!midissue
||(o_wb_stb && last_stb && !i_wb_stall))
&&(skid_arvalid)
// One ID at a time, lest we return a bus error
// to the wrong AXI master
&&(wb_empty ||(skid_arid == axi_id));
initial o_wb_cyc = 0;
initial o_wb_stb = 0;
initial stblen = 0;
initial last_stb = 0;
always @(posedge S_AXI_ACLK)
if (w_reset)
begin
o_wb_stb <= 1'b0;
o_wb_cyc <= 1'b0;
end else if (err_state || (o_wb_cyc && i_wb_err))
begin
o_wb_cyc <= 1'b0;
o_wb_stb <= 1'b0;
end else if ((!o_wb_stb)||(!i_wb_stall))
begin
if (accept_request)
begin
// Process the new request
o_wb_cyc <= 1'b1;
if (lastid_fifo_full && (!S_AXI_RVALID||!S_AXI_RREADY))
o_wb_stb <= 1'b0;
else if (fifo_nearfull && midissue
&& (!S_AXI_RVALID||!S_AXI_RREADY))
o_wb_stb <= 1'b0;
else
o_wb_stb <= 1'b1;
end else if (!o_wb_stb && midissue)
begin
// Restart a transfer once the FIFO clears
if (S_AXI_RVALID)
o_wb_stb <= S_AXI_RREADY;
// end else if ((o_wb_cyc)&&(i_wb_err)||(err_state))
end else if (o_wb_stb && !last_stb)
begin
if (fifo_nearfull
&& (!S_AXI_RVALID||!S_AXI_RREADY))
o_wb_stb <= 1'b0;
end else if (!o_wb_stb || last_stb)
begin
// End the request
o_wb_stb <= 1'b0;
// Check for the last acknowledgment
if ((i_wb_ack)&&(last_ack))
o_wb_cyc <= 1'b0;
if (i_wb_err)
o_wb_cyc <= 1'b0;
end
end
initial stblen = 0;
initial last_stb = 0;
initial midissue = 0;
always @(posedge S_AXI_ACLK)
if (w_reset)
begin
stblen <= 0;
last_stb <= 0;
midissue <= 0;
end else if (accept_request)
begin
stblen <= skid_arlen;
last_stb <= (skid_arlen == 0);
midissue <= 1'b1;
end else if (lastid_fifo_wr)
begin
if (stblen > 0)
stblen <= stblen - 1;
last_stb <= (stblen == 1);
midissue <= (stblen > 0);
end
always @(posedge S_AXI_ACLK)
if (accept_request)
begin
axi_id <= skid_arid;
axi_burst <= skid_arburst;
axi_size <= skid_arsize;
axi_len <= skid_arlen;
end
axi_addr #(.AW(C_AXI_ADDR_WIDTH), .DW(C_AXI_DATA_WIDTH))
next_read_addr(axi_addr, axi_size, axi_burst, axi_len, next_addr);
always @(posedge S_AXI_ACLK)
if (accept_request)
axi_addr <= skid_araddr;
else if (!i_wb_stall)
axi_addr <= next_addr;
always @(*)
o_wb_addr = axi_addr[(C_AXI_ADDR_WIDTH-1):C_AXI_ADDR_WIDTH-AW];
assign lastid_fifo_wr = (o_wb_stb && (i_wb_err || !i_wb_stall))
||(err_state && midissue && !lastid_fifo_full);
sfifo #(.BW(C_AXI_ID_WIDTH+1), .LGFLEN(LGFIFO))
lastid_fifo(S_AXI_ACLK, w_reset,
lastid_fifo_wr,
{ axi_id, last_stb },
lastid_fifo_full, lastid_fifo_fill,
S_AXI_RVALID && S_AXI_RREADY,
{ S_AXI_RID, S_AXI_RLAST },
lastid_fifo_empty);
reg [C_AXI_DATA_WIDTH-1:0] read_data;
generate if (OPT_SWAP_ENDIANNESS)
begin : SWAP_ENDIANNESS
integer ik;
// AXI is little endian. WB can be either. Most of my WB
// work is big-endian.
//
// This will convert byte ordering between the two
always @(*)
for(ik=0; ik<C_AXI_DATA_WIDTH/8; ik=ik+1)
read_data[ik*8 +: 8]
= i_wb_data[(C_AXI_DATA_WIDTH-(ik+1)*8) +: 8];
end else begin : KEEP_ENDIANNESS
always @(*)
read_data = i_wb_data;
end endgenerate
sfifo #(.BW(C_AXI_DATA_WIDTH+1), .LGFLEN(LGFIFO))
resp_fifo(S_AXI_ACLK, w_reset,
o_wb_cyc && (i_wb_ack || i_wb_err), { read_data, i_wb_err },
resp_fifo_full, resp_fifo_fill,
S_AXI_RVALID && S_AXI_RREADY,
{ S_AXI_RDATA, response_err },
resp_fifo_empty);
// Count the number of acknowledgements we are still expecting. This
// is to support the last_ack calculation in the next process
initial acks_expected = 0;
always @(posedge S_AXI_ACLK)
if (!S_AXI_ARESETN || err_state)
acks_expected <= 0;
else case({ accept_request, o_wb_cyc && i_wb_ack })
2'b01: acks_expected <= acks_expected -1;
2'b10: acks_expected <= acks_expected+({{(LGFIFO){1'b0}},skid_arlen} + 1);
2'b11: acks_expected <= acks_expected+{{(LGFIFO){1'b0}},skid_arlen};
default: begin end
endcase
// last_ack should be true if the next acknowledgment will end the bus
// cycle
initial last_ack = 1;
always @(posedge S_AXI_ACLK)
if (!S_AXI_ARESETN || err_state)
last_ack <= 1;
else case({ accept_request, i_wb_ack })
2'b01: last_ack <= (acks_expected <= 2);
2'b10: last_ack <= (acks_expected == 0)&&(skid_arlen == 0);
2'b11: last_ack <= (acks_expected < 2)&&(skid_arlen < 2)
&&(!acks_expected[0]||!skid_arlen[0]);
default: begin end
endcase
initial fifo_nearfull = 1'b0;
always @(posedge S_AXI_ACLK)
if (!S_AXI_ARESETN)
fifo_nearfull <= 1'b0;
else case({ lastid_fifo_wr, S_AXI_RVALID && S_AXI_RREADY })
2'b10: fifo_nearfull <= (lastid_fifo_fill >= (1<<LGFIFO)-2);
2'b01: fifo_nearfull <= lastid_fifo_full;
default: begin end
endcase
initial wb_empty = 1'b1;
always @(posedge S_AXI_ACLK)
if (!S_AXI_ARESETN || !o_wb_cyc)
wb_empty <= 1'b1;
else case({ lastid_fifo_wr, (i_wb_ack || i_wb_err) })
2'b10: wb_empty <= 1'b0;
2'b01: wb_empty <= (lastid_fifo_fill == resp_fifo_fill + 1);
default: begin end
endcase
always @(*)
begin
S_AXI_RRESP[0] = 1'b0;
S_AXI_RRESP[1] = response_err || (resp_fifo_empty && err_state);
S_AXI_RVALID = !resp_fifo_empty
|| (err_state && !lastid_fifo_empty);
end
initial err_state = 0;
always @(posedge S_AXI_ACLK)
if (w_reset)
err_state <= 1'b0;
else if ((o_wb_cyc)&&(i_wb_err))
err_state <= 1'b1;
else if (lastid_fifo_empty && !midissue)
err_state <= 1'b0;
// Make Verilator happy
// verilator lint_off UNUSED
wire unused;
assign unused = &{ 1'b0, S_AXI_ARLOCK, S_AXI_ARCACHE,
S_AXI_ARPROT, S_AXI_ARQOS, resp_fifo_full };
// verilator lint_on UNUSED
`ifdef FORMAL
////////////////////////////////////////////////////////////////////////
//
// The following are a subset of the properties used to verify this
// core.
//
////////////////////////////////////////////////////////////////////////
//
//
reg f_past_valid;
initial f_past_valid = 1'b0;
always @(posedge S_AXI_ACLK)
f_past_valid <= 1'b1;
////////////////////////////////////////////////////////////////////////
//
// Assumptions
//
//
always @(*)
if (!f_past_valid)
assume(w_reset);
////////////////////////////////////////////////////////////////////////
//
// Ad-hoc assertions
//
//
////////////////////////////////////////////////////////////////////////
//
// Error state
//
//
always @(*)
if (err_state)
assert(!o_wb_stb && !o_wb_cyc);
////////////////////////////////////////////////////////////////////////
//
// Bus properties
//
//
localparam F_LGDEPTH = (LGFIFO>8) ? LGFIFO+1 : 10, F_LGRDFIFO = 72; // 9*F_LGFIFO;
//
// ...
//
wire [(F_LGDEPTH-1):0]
fwb_nreqs, fwb_nacks, fwb_outstanding;
fwb_master #(.AW(AW), .DW(C_AXI_DATA_WIDTH), .F_MAX_STALL(2),
.F_MAX_ACK_DELAY(3), .F_LGDEPTH(F_LGDEPTH),
.F_OPT_DISCONTINUOUS(1))
fwb(S_AXI_ACLK, w_reset,
o_wb_cyc, o_wb_stb, 1'b0, o_wb_addr, {(DW){1'b0}}, 4'hf,
i_wb_ack, i_wb_stall, i_wb_data, i_wb_err,
fwb_nreqs, fwb_nacks, fwb_outstanding);
always @(*)
if (err_state)
assert(fwb_outstanding == 0);
faxi_slave #(.C_AXI_DATA_WIDTH(C_AXI_DATA_WIDTH),
.C_AXI_ADDR_WIDTH(C_AXI_ADDR_WIDTH),
.C_AXI_ID_WIDTH(C_AXI_ID_WIDTH),
.F_LGDEPTH(F_LGDEPTH),
.F_AXI_MAXSTALL(0),
.F_AXI_MAXDELAY(0))
faxi(.i_clk(S_AXI_ACLK), .i_axi_reset_n(S_AXI_ARESETN),
.i_axi_awready(1'b0),
.i_axi_awaddr(0),
.i_axi_awlen(8'h0),
.i_axi_awsize(3'h0),
.i_axi_awburst(2'h0),
.i_axi_awlock(1'b0),
.i_axi_awcache(4'h0),
.i_axi_awprot(3'h0),
.i_axi_awqos(4'h0),
.i_axi_awvalid(1'b0),
//
.i_axi_wready(1'b0),
.i_axi_wdata(0),
.i_axi_wstrb(0),
.i_axi_wlast(0),
.i_axi_wvalid(1'b0),
//
.i_axi_bid(0),
.i_axi_bresp(0),
.i_axi_bvalid(1'b0),
.i_axi_bready(1'b0),
//
.i_axi_arready(S_AXI_ARREADY),
.i_axi_arid(S_AXI_ARID),
.i_axi_araddr(S_AXI_ARADDR),
.i_axi_arlen(S_AXI_ARLEN),
.i_axi_arsize(S_AXI_ARSIZE),
.i_axi_arburst(S_AXI_ARBURST),
.i_axi_arlock(S_AXI_ARLOCK),
.i_axi_arcache(S_AXI_ARCACHE),
.i_axi_arprot(S_AXI_ARPROT),
.i_axi_arqos(S_AXI_ARQOS),
.i_axi_arvalid(S_AXI_ARVALID),
//
.i_axi_rresp(S_AXI_RRESP),
.i_axi_rid(S_AXI_RID),
.i_axi_rvalid(S_AXI_RVALID),
.i_axi_rdata(S_AXI_RDATA),
.i_axi_rlast(S_AXI_RLAST),
.i_axi_rready(S_AXI_RREADY)
//
// ...
//
);
//
// ...
//
always @(*)
if (!resp_fifo_empty && response_err)
assert(resp_fifo_fill == 1);
always @(*)
assert(midissue == ((stblen > 0)||(last_stb)));
always @(*)
if (last_stb && !err_state)
assert(o_wb_stb || lastid_fifo_full);
always @(*)
if (last_stb)
assert(stblen == 0);
always @(*)
if (lastid_fifo_full)
begin
assert(!o_wb_stb);
assert(!lastid_fifo_wr);
end
always @(*)
if (!err_state)
begin
if (midissue && !last_stb)
assert(!last_ack);
assert(lastid_fifo_fill - resp_fifo_fill
== fwb_outstanding);
if (fwb_outstanding > 1)
assert(!last_ack);
else if (fwb_outstanding == 1)
assert(midissue || last_ack);
else if (o_wb_cyc) // && (fwb_outstanding ==0)
assert(last_ack == last_stb);
if (midissue)
assert(o_wb_cyc);
end
always @(*)
if (o_wb_cyc)
assert(wb_empty == (lastid_fifo_fill == resp_fifo_fill));
always @(*)
if ((fwb_nacks == fwb_nreqs)&&(!midissue))
assert(!o_wb_cyc);
always @(*)
assert(fwb_outstanding <= (1<<LGFIFO));
always @(*)
assert(fifo_nearfull == (lastid_fifo_fill >= (1<<LGFIFO)-1));
always @(*)
if (o_wb_stb)
assert(last_ack == (last_stb&& wb_empty));//&&(!i_wb_stall);
else if (o_wb_cyc && !midissue)
assert(last_ack == (resp_fifo_fill + 1 >= lastid_fifo_fill));
////////////////////////////////////////////////////////////////////////
//
// Cover checks
//
////////////////////////////////////////////////////////////////////////
//
//
reg [3:0] cvr_reads, cvr_read_bursts, cvr_rdid_bursts;
reg [C_AXI_ID_WIDTH-1:0] cvr_read_id;
initial cvr_reads = 0;
always @(posedge S_AXI_ACLK)
if (!S_AXI_ARESETN)
cvr_reads <= 1;
else if (i_wb_err)
cvr_reads <= 0;
else if (S_AXI_RVALID && S_AXI_RREADY && !cvr_reads[3]
&& cvr_reads > 0)
cvr_reads <= cvr_reads + 1;
initial cvr_read_bursts = 0;
always @(posedge S_AXI_ACLK)
if (!S_AXI_ARESETN)
cvr_read_bursts <= 1;
else if (S_AXI_ARVALID && S_AXI_ARLEN < 1)
cvr_read_bursts <= 0;
else if (i_wb_err)
cvr_read_bursts <= 0;
else if (S_AXI_RVALID && S_AXI_RREADY && S_AXI_RLAST
&& !cvr_read_bursts[3] && cvr_read_bursts > 0)
cvr_read_bursts <= cvr_read_bursts + 1;
initial cvr_read_id = 0;
always @(posedge S_AXI_ACLK)
if (!S_AXI_ARESETN)
cvr_read_id <= 1;
else if (S_AXI_RVALID && S_AXI_RREADY && S_AXI_RLAST)
cvr_read_id <= cvr_read_id + 1;
initial cvr_rdid_bursts = 0;
always @(posedge S_AXI_ACLK)
if (!S_AXI_ARESETN)
cvr_rdid_bursts <= 1;
else if (S_AXI_ARVALID && S_AXI_ARLEN < 1)
cvr_rdid_bursts <= 0;
else if (i_wb_err)
cvr_rdid_bursts <= 0;
else if (S_AXI_RVALID && S_AXI_RREADY && S_AXI_RLAST
&& S_AXI_RID == cvr_read_id
&& !cvr_rdid_bursts[3] && cvr_rdid_bursts > 0)
cvr_rdid_bursts <= cvr_rdid_bursts + 1;
always @(*)
cover(cvr_reads == 4);
always @(*)
cover(cvr_read_bursts == 4);
always @(*)
cover(cvr_rdid_bursts == 4);
`endif
endmodule