blob: a16d89a17d118ecd0b05c517f3085445569674ac [file] [log] [blame]
////////////////////////////////////////////////////////////////////////////////
//
// Filename: aximwr2wbsp.v
//
// Project: WB2AXIPSP: bus bridges and other odds and ends
//
// Purpose: Convert the three AXI4 write channels to a single wishbone
// channel to write the results.
//
//
// Creator: Dan Gisselquist, Ph.D.
// Gisselquist Technology, LLC
//
////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2015-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 aximwr2wbsp #(
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 [0:0] OPT_SWAP_ENDIANNESS = 1'b0, // Lil to Big Endian swap
localparam AXI_LSBS = $clog2(C_AXI_DATA_WIDTH)-3,
localparam AW = C_AXI_ADDR_WIDTH-AXI_LSBS,
localparam DW = C_AXI_DATA_WIDTH,
parameter LGFIFO = 5
) (
input wire S_AXI_ACLK, // System clock
input wire S_AXI_ARESETN,
// AXI write address channel signals
input wire S_AXI_AWVALID, // Write address valid
output wire S_AXI_AWREADY, // Slave is ready to accept
input wire [C_AXI_ID_WIDTH-1:0] S_AXI_AWID, // Write ID
input wire [C_AXI_ADDR_WIDTH-1:0] S_AXI_AWADDR, // Write address
input wire [7:0] S_AXI_AWLEN, // Write Burst Length
input wire [2:0] S_AXI_AWSIZE, // Write Burst size
input wire [1:0] S_AXI_AWBURST, // Write Burst type
input wire [0:0] S_AXI_AWLOCK, // Write lock type
input wire [3:0] S_AXI_AWCACHE, // Write Cache type
input wire [2:0] S_AXI_AWPROT, // Write Protection type
input wire [3:0] S_AXI_AWQOS, // Write Quality of Svc
// AXI write data channel signals
input wire S_AXI_WVALID, // Write valid
output wire S_AXI_WREADY, // Write data ready
input wire [C_AXI_DATA_WIDTH-1:0] S_AXI_WDATA, // Write data
input wire [C_AXI_DATA_WIDTH/8-1:0] S_AXI_WSTRB, // Write strobes
input wire S_AXI_WLAST, // Last write transaction
// AXI write response channel signals
output wire S_AXI_BVALID, // Write reponse valid
input wire S_AXI_BREADY, // Response ready
output wire [C_AXI_ID_WIDTH-1:0] S_AXI_BID, // Response ID
output wire [1:0] S_AXI_BRESP, // Write 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,
output reg [(C_AXI_DATA_WIDTH-1):0] o_wb_data,
output reg [(C_AXI_DATA_WIDTH/8-1):0] o_wb_sel,
input wire i_wb_stall,
input wire i_wb_ack,
// input [(C_AXI_DATA_WIDTH-1):0] i_wb_data,
input wire i_wb_err
);
wire w_reset;
wire skid_awvalid;
reg accept_write_burst;
wire [C_AXI_ID_WIDTH-1:0] skid_awid;
wire [C_AXI_ADDR_WIDTH-1:0] skid_awaddr;
wire [7:0] skid_awlen;
wire [2:0] skid_awsize;
wire [1:0] skid_awburst;
//
wire skid_wvalid, skid_wlast;
reg skid_wready;
wire [C_AXI_DATA_WIDTH-1:0] skid_wdata;
wire [C_AXI_DATA_WIDTH/8-1:0] skid_wstrb;
reg skid_awready;
reg [7:0] axi_wlen, wlen;
reg [C_AXI_ID_WIDTH-1:0] axi_wid;
reg [C_AXI_ADDR_WIDTH-1:0] axi_waddr;
wire [C_AXI_ADDR_WIDTH-1:0] next_addr;
reg [1:0] axi_wburst;
reg [2:0] axi_wsize;
reg [LGFIFO+7:0] acks_expected;
reg [LGFIFO:0] writes_expected;
reg last_ack;
reg err_state;
reg read_ack_fifo;
wire [7:0] fifo_ack_ln;
reg [8:0] acklen;
reg ack_last, ack_err, ack_empty;
reg [LGFIFO:0] total_fifo_fill;
reg total_fifo_full;
wire wb_ack_fifo_full, wb_ack_fifo_empty;
wire [LGFIFO:0] wb_ack_fifo_fill;
wire err_fifo_full, err_fifo_empty;
wire [LGFIFO:0] err_fifo_fill;
reg err_fifo_write;
wire bid_fifo_full, bid_fifo_empty;
wire [LGFIFO:0] bid_fifo_fill;
assign w_reset = (S_AXI_ARESETN == 1'b0);
// Step 1: a pair of skid buffers
skidbuffer #(
.OPT_OUTREG(0),
.DW(C_AXI_ADDR_WIDTH+C_AXI_ID_WIDTH+8+3+2))
awskid(S_AXI_ACLK, !S_AXI_ARESETN, S_AXI_AWVALID, S_AXI_AWREADY,
{ S_AXI_AWID, S_AXI_AWADDR, S_AXI_AWLEN,
S_AXI_AWSIZE, S_AXI_AWBURST },
skid_awvalid, accept_write_burst,
{ skid_awid, skid_awaddr, skid_awlen,
skid_awsize, skid_awburst });
skidbuffer #(
.OPT_OUTREG(0),
.DW(C_AXI_DATA_WIDTH + C_AXI_DATA_WIDTH/8+1))
wskid(S_AXI_ACLK, !S_AXI_ARESETN,
S_AXI_WVALID, S_AXI_WREADY,
{ S_AXI_WDATA, S_AXI_WSTRB, S_AXI_WLAST },
skid_wvalid, skid_wready,
{ skid_wdata, skid_wstrb, skid_wlast });
always @(*)
begin
accept_write_burst = (skid_awready)&&(!o_wb_stb || !i_wb_stall)
&&(!err_state)&&(skid_awvalid)
&&(!total_fifo_full);
if (axi_wid != skid_awid && (acks_expected > 0))
accept_write_burst = 0;
if (!skid_wvalid)
accept_write_burst = 0;
end
always @(*)
skid_wready = (!o_wb_stb || !i_wb_stall || err_state)
&&(!skid_awready || accept_write_burst);
initial skid_awready = 1'b1;
always @(posedge S_AXI_ACLK)
if (!S_AXI_ARESETN)
skid_awready <= 1'b1;
else if (accept_write_burst)
skid_awready <= (skid_awlen == 0)&&(skid_wvalid)&&(skid_wlast);
else if (skid_wvalid && skid_wready && skid_wlast)
skid_awready <= 1'b1;
always @(posedge S_AXI_ACLK)
if (accept_write_burst)
begin
axi_wid <= skid_awid;
axi_waddr <= skid_awaddr;
axi_wsize <= skid_awsize;
axi_wburst <= skid_awburst;
axi_wlen <= skid_awlen;
wlen <= skid_awlen;
end else if (skid_wvalid && skid_wready)
begin
axi_waddr <= next_addr;
if (!skid_awready)
wlen <= wlen - 1;
end
axi_addr #(.AW(C_AXI_ADDR_WIDTH), .DW(C_AXI_DATA_WIDTH))
next_write_addr(axi_waddr, axi_wsize, axi_wburst, axi_wlen, next_addr);
initial { o_wb_cyc, o_wb_stb } = 0;
always @(posedge S_AXI_ACLK)
if (!S_AXI_ARESETN || err_state || (o_wb_cyc && i_wb_err))
begin
o_wb_cyc <= 1'b0;
o_wb_stb <= 1'b0;
end else if (accept_write_burst)
begin
o_wb_cyc <= 1'b1;
o_wb_stb <= skid_wvalid && skid_wready;
end else begin
if (!o_wb_stb || !i_wb_stall)
o_wb_stb <= (!skid_awready)&&(skid_wvalid&&skid_wready);
if (o_wb_cyc && last_ack && i_wb_ack && !skid_awvalid)
o_wb_cyc <= 0;
end
always @(*)
o_wb_addr = axi_waddr[C_AXI_ADDR_WIDTH-1:AXI_LSBS];
generate if (OPT_SWAP_ENDIANNESS)
begin : SWAP_ENDIANNESS
integer ik;
always @(posedge S_AXI_ACLK)
if (!o_wb_stb || !i_wb_stall)
begin
for(ik=0; ik<DW/8; ik=ik+1)
begin
o_wb_data[ik*8 +: 8]
<= skid_wdata[(DW/8-1-ik)*8 +: 8];
o_wb_sel[ik] <= skid_wstrb[DW/8-1-ik];
end
end
end else begin : KEEP_ENDIANNESS
always @(posedge S_AXI_ACLK)
if (!o_wb_stb || !i_wb_stall)
begin
o_wb_data <= skid_wdata;
o_wb_sel <= skid_wstrb;
end
end endgenerate
initial writes_expected = 0;
always @(posedge S_AXI_ACLK)
if (!S_AXI_ARESETN)
begin
writes_expected <= 0;
end else case({skid_wvalid && skid_wready && skid_wlast,
S_AXI_BVALID && S_AXI_BREADY })
2'b01: writes_expected <= writes_expected - 1;
2'b10: writes_expected <= writes_expected + 1;
default: begin end
endcase
initial acks_expected = 0;
always @(posedge S_AXI_ACLK)
if (!S_AXI_ARESETN || i_wb_err || err_state)
begin
acks_expected <= 0;
end else case({skid_awvalid && accept_write_burst, {i_wb_ack|i_wb_err}})
2'b01: acks_expected <= acks_expected - 1;
2'b10: acks_expected <= acks_expected + ({{(LGFIFO){1'b0}},skid_awlen} + 1);
2'b11: acks_expected <= acks_expected + {{(LGFIFO){1'b0}},skid_awlen};
default: begin end
endcase
initial last_ack = 1;
always @(posedge S_AXI_ACLK)
if (!S_AXI_ARESETN || i_wb_err || err_state)
begin
last_ack <= 1;
end else case({skid_awvalid && accept_write_burst, i_wb_ack })
2'b01: last_ack <= (acks_expected <= 2);
2'b10: last_ack <= (acks_expected == 0)&&(skid_awlen == 0);
2'b11: last_ack <= last_ack && (skid_awlen == 0);
default: begin end
endcase
initial total_fifo_fill = 0;
always @(posedge S_AXI_ACLK)
if (!S_AXI_ARESETN)
total_fifo_fill <= 0;
else case({ accept_write_burst, S_AXI_BVALID && S_AXI_BREADY })
2'b01: total_fifo_fill <= total_fifo_fill - 1;
2'b10: total_fifo_fill <= total_fifo_fill + 1;
default: begin end
endcase
always @(*)
total_fifo_full = total_fifo_fill[LGFIFO];
sfifo #(.BW(8), .LGFLEN(LGFIFO),
.OPT_ASYNC_READ(1'b1))
wb_ack_fifo(S_AXI_ACLK, !S_AXI_ARESETN,
accept_write_burst, skid_awlen,
wb_ack_fifo_full, wb_ack_fifo_fill,
read_ack_fifo, fifo_ack_ln, wb_ack_fifo_empty);
always @(*)
begin
read_ack_fifo = ack_last && (i_wb_ack || i_wb_err);
if (err_state || ack_empty)
read_ack_fifo = 1;
if (wb_ack_fifo_empty)
read_ack_fifo = 1'b0;
end
reg [8:0] next_acklen;
reg [1:0] next_acklow;
always @(*)
next_acklen = fifo_ack_ln + ((acklen[0] ? 1:0)
+ ((i_wb_ack|i_wb_err)? 0:1));
always @(*)
next_acklow = fifo_ack_ln[0] + ((acklen[0] ? 1:0)
+ ((i_wb_ack|i_wb_err)? 0:1));
initial acklen = 0;
initial ack_last = 0;
initial ack_empty = 1;
always @(posedge S_AXI_ACLK)
if (!S_AXI_ARESETN || err_state)
begin
acklen <= 0;
ack_last <= 0;
ack_empty<= 1;
end else if (read_ack_fifo)
begin
acklen <= next_acklen;
ack_last <= (fifo_ack_ln < 2)&&(next_acklow == 1);
ack_empty<= (fifo_ack_ln == 0)&&(!acklen[0])
&&(i_wb_ack || i_wb_err);
end else if (i_wb_ack || i_wb_err)
begin
if (acklen > 0)
acklen <= acklen - 1;
ack_last <= (acklen == 2);
ack_empty <= ack_last;
end
always @(posedge S_AXI_ACLK)
if (read_ack_fifo)
begin
ack_err <= (wb_ack_fifo_empty) || err_state || i_wb_err;
end else if (i_wb_ack || i_wb_err || err_state)
ack_err <= ack_err || (i_wb_err || err_state);
initial err_state = 0;
always @(posedge S_AXI_ACLK)
if (!S_AXI_ARESETN)
err_state <= 0;
else if (o_wb_cyc && i_wb_err)
err_state <= 1;
else if ((total_fifo_fill == bid_fifo_fill)
&&(total_fifo_fill == err_fifo_fill))
err_state <= 0;
initial err_fifo_write = 0;
always @(posedge S_AXI_ACLK)
if (!S_AXI_ARESETN)
err_fifo_write <= 0;
else if (read_ack_fifo && ack_empty && fifo_ack_ln == 0)
err_fifo_write <= (i_wb_ack || i_wb_err || err_state);
else if (ack_last)
err_fifo_write <= (i_wb_ack || i_wb_err || err_state);
else
err_fifo_write <= 1'b0;
sfifo #(.BW(C_AXI_ID_WIDTH), .LGFLEN(LGFIFO))
bid_fifo(S_AXI_ACLK, !S_AXI_ARESETN,
skid_wvalid && skid_wready && skid_wlast,
(total_fifo_fill == bid_fifo_fill) ? skid_awid:axi_wid,
bid_fifo_full, bid_fifo_fill,
S_AXI_BVALID & S_AXI_BREADY, S_AXI_BID, bid_fifo_empty);
sfifo #(.BW(1), .LGFLEN(LGFIFO))
err_fifo(S_AXI_ACLK, !S_AXI_ARESETN,
err_fifo_write, { ack_err || i_wb_err },
err_fifo_full, err_fifo_fill,
S_AXI_BVALID & S_AXI_BREADY, S_AXI_BRESP[1], err_fifo_empty);
assign S_AXI_BVALID = !bid_fifo_empty && !err_fifo_empty;
assign S_AXI_BRESP[0]= 1'b0;
// Make Verilator happy
// verilator lint_on UNUSED
wire unused;
assign unused = &{ 1'b0, S_AXI_AWBURST, S_AXI_AWSIZE,
S_AXI_AWLOCK, S_AXI_AWCACHE, S_AXI_AWPROT,
S_AXI_AWQOS, S_AXI_WLAST,
wb_ack_fifo_full, wb_ack_fifo_fill,
bid_fifo_full, err_fifo_full,
w_reset
};
// verilator lint_off 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;
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'b1, o_wb_addr, o_wb_data, o_wb_sel,
i_wb_ack, i_wb_stall, {(DW){1'b0}}, i_wb_err,
fwb_nreqs, fwb_nacks, fwb_outstanding);
//
// ...
//
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(S_AXI_AWREADY),
.i_axi_awid( S_AXI_AWID),
.i_axi_awaddr( S_AXI_AWADDR),
.i_axi_awlen( S_AXI_AWLEN),
.i_axi_awsize( S_AXI_AWSIZE),
.i_axi_awburst(S_AXI_AWBURST),
.i_axi_awlock( S_AXI_AWLOCK),
.i_axi_awcache(S_AXI_AWCACHE),
.i_axi_awprot( S_AXI_AWPROT),
.i_axi_awqos( S_AXI_AWQOS),
.i_axi_awvalid(S_AXI_AWVALID),
//
.i_axi_wready(S_AXI_WREADY),
.i_axi_wdata( S_AXI_WDATA),
.i_axi_wstrb( S_AXI_WSTRB),
.i_axi_wlast( S_AXI_WLAST),
.i_axi_wvalid(S_AXI_WVALID),
//
.i_axi_bid( S_AXI_BID),
.i_axi_bresp( S_AXI_BRESP),
.i_axi_bvalid(S_AXI_BVALID),
.i_axi_bready(S_AXI_BREADY),
//
.i_axi_arready(1'b0),
.i_axi_arid( {(C_AXI_ID_WIDTH){1'b0}}),
.i_axi_araddr({(C_AXI_ADDR_WIDTH){1'b0}}),
.i_axi_arlen( 8'h0),
.i_axi_arsize( 3'h0),
.i_axi_arburst(2'h0),
.i_axi_arlock( 1'b0),
.i_axi_arcache(4'h0),
.i_axi_arprot( 3'h0),
.i_axi_arqos( 4'h0),
.i_axi_arvalid(1'b0),
//
.i_axi_rresp( 2'h0),
.i_axi_rid( {(C_AXI_ID_WIDTH){1'b0}}),
.i_axi_rvalid(1'b0),
.i_axi_rdata( {(C_AXI_DATA_WIDTH){1'b0}}),
.i_axi_rlast( 1'b0),
.i_axi_rready(1'b0)
//
// ...
//
);
(* anyconst *) reg never_err;
always @(*)
if (never_err)
begin
assume(!i_wb_err);
assert(!err_state);
if (!skid_awvalid)
assert(o_wb_cyc == (acks_expected != 0));
if (!skid_awready)
assert(o_wb_cyc);
if (S_AXI_BVALID)
assert(!S_AXI_BRESP[1]);
assert(!S_AXI_BRESP[0]);
end
////////////////////////////////////////////////////////////////////////
//
// Cover checks
//
////////////////////////////////////////////////////////////////////////
//
//
reg [3:0] cvr_writes, cvr_write_bursts, cvr_wrid_bursts;
reg [C_AXI_ID_WIDTH-1:0] cvr_write_id;
initial cvr_writes = 0;
always @(posedge S_AXI_ACLK)
if (!S_AXI_ARESETN)
cvr_writes <= 1;
else if (i_wb_err)
cvr_writes <= 0;
else if (S_AXI_BVALID && S_AXI_BREADY && !cvr_writes[3]
&& cvr_writes > 0)
cvr_writes <= cvr_writes + 1;
initial cvr_write_bursts = 0;
always @(posedge S_AXI_ACLK)
if (!S_AXI_ARESETN)
cvr_write_bursts <= 1;
else if (S_AXI_AWVALID && S_AXI_AWLEN < 1)
cvr_write_bursts <= 0;
else if (i_wb_err)
cvr_write_bursts <= 0;
else if (S_AXI_BVALID && S_AXI_BREADY
&& !cvr_write_bursts[3] && cvr_write_bursts > 0)
cvr_write_bursts <= cvr_write_bursts + 1;
initial cvr_write_id = 0;
always @(posedge S_AXI_ACLK)
if (!S_AXI_ARESETN)
cvr_write_id <= 1;
else if (S_AXI_BVALID && S_AXI_BREADY)
cvr_write_id <= cvr_write_id + 1;
initial cvr_wrid_bursts = 0;
always @(posedge S_AXI_ACLK)
if (!S_AXI_ARESETN)
cvr_wrid_bursts <= 1;
else if (S_AXI_AWVALID && S_AXI_AWLEN < 1)
cvr_wrid_bursts <= 0;
else if (i_wb_err)
cvr_wrid_bursts <= 0;
else if (S_AXI_BVALID && S_AXI_BREADY
&& S_AXI_BID == cvr_write_id
&& !cvr_wrid_bursts[3] && cvr_wrid_bursts > 0)
cvr_wrid_bursts <= cvr_wrid_bursts + 1;
always @(*)
cover(cvr_writes == 4);
always @(*)
cover(cvr_write_bursts == 4);
always @(*)
cover(cvr_wrid_bursts == 4);
`endif
endmodule