blob: f3d63e20fcfeaf2de086a113d86ecfbe87790bea [file] [log] [blame]
////////////////////////////////////////////////////////////////////////////////
//
// Filename: wbm2axilite.v (Wishbone master to AXI slave, pipelined)
//
// Project: WB2AXIPSP: bus bridges and other odds and ends
//
// Purpose: Convert from a wishbone master to an AXI lite interface. The
// big difference is that AXI lite doesn't support bursting,
// or transaction ID's. This actually makes the task a *LOT* easier.
//
// Creator: Dan Gisselquist, Ph.D.
// Gisselquist Technology, LLC
//
////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2018-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 wbm2axilite #(
parameter C_AXI_ADDR_WIDTH = 28,// AXI Address width
localparam C_AXI_DATA_WIDTH = 32,// Width of the AXI R&W data
localparam DW = C_AXI_DATA_WIDTH,// Wishbone data width
localparam AW = C_AXI_ADDR_WIDTH-2// WB addr width (log wordsize)
) (
input wire i_clk,
input wire i_reset,
//
// We'll share the clock and the reset
input wire i_wb_cyc,
input wire i_wb_stb,
input wire i_wb_we,
input wire [(AW-1):0] i_wb_addr,
input wire [(DW-1):0] i_wb_data,
input wire [(DW/8-1):0] i_wb_sel,
output wire o_wb_stall,
output reg o_wb_ack,
output reg [(DW-1):0] o_wb_data,
output reg o_wb_err,
//
// AXI write address channel signals
output reg o_axi_awvalid,
input wire i_axi_awready,
output reg [C_AXI_ADDR_WIDTH-1:0] o_axi_awaddr,
output wire [2:0] o_axi_awprot,
//
// AXI write data channel signals
output reg o_axi_wvalid,
input wire i_axi_wready,
output reg [C_AXI_DATA_WIDTH-1:0] o_axi_wdata,
output reg [C_AXI_DATA_WIDTH/8-1:0] o_axi_wstrb,
//
// AXI write response channel signals
input wire i_axi_bvalid,
output wire o_axi_bready,
input wire [1:0] i_axi_bresp,
//
// AXI read address channel signals
output reg o_axi_arvalid,
input wire i_axi_arready,
output reg [C_AXI_ADDR_WIDTH-1:0] o_axi_araddr,
output wire [2:0] o_axi_arprot,
//
// AXI read data channel signals
input wire i_axi_rvalid,
output wire o_axi_rready,
input wire [C_AXI_DATA_WIDTH-1:0] i_axi_rdata,
input wire [1:0] i_axi_rresp
);
//*****************************************************************************
// Local Parameter declarations
//*****************************************************************************
localparam LG_AXI_DW = ( C_AXI_DATA_WIDTH == 8) ? 3
: ((C_AXI_DATA_WIDTH == 16) ? 4
: ((C_AXI_DATA_WIDTH == 32) ? 5
: ((C_AXI_DATA_WIDTH == 64) ? 6
: ((C_AXI_DATA_WIDTH == 128) ? 7
: 8))));
localparam LG_WB_DW = ( DW == 8) ? 3
: ((DW == 16) ? 4
: ((DW == 32) ? 5
: ((DW == 64) ? 6
: ((DW == 128) ? 7
: 8))));
//
// LGIFOFLN: The log (based two) of the size of our FIFO. This is a
// localparam since 1) 32-bit distributed memories nearly come for
// free, and 2) because there is no performance gain to be had in larger
// memories. 2^32 entries is the perfect size for this application.
// Any smaller, and the core will not be able to maintain 100%
// throughput.
localparam LGFIFOLN = 5;
localparam FIFOLN = (1<<LGFIFOLN);
//*****************************************************************************
// Internal register and wire declarations
//*****************************************************************************
// Things we're not changing ...
assign o_axi_awprot = 3'b000; // Unpriviledged, unsecure, data access
assign o_axi_arprot = 3'b000; // Unpriviledged, unsecure, data access
reg full_fifo, err_state, axi_reset_state, wb_we;
reg [3:0] reset_count;
reg pending;
reg [LGFIFOLN-1:0] outstanding, err_pending;
// Master bridge logic
assign o_wb_stall = (full_fifo)
||((!i_wb_we)&&( wb_we)&&(pending))
||(( i_wb_we)&&(!wb_we)&&(pending))
||(err_state)||(axi_reset_state)
||(o_axi_arvalid)&&(!i_axi_arready)
||(o_axi_awvalid)&&(!i_axi_awready)
||(o_axi_wvalid)&&(!i_axi_wready);
initial axi_reset_state = 1'b1;
initial reset_count = 4'hf;
always @(posedge i_clk)
if (i_reset)
begin
axi_reset_state <= 1'b1;
if (reset_count > 0)
reset_count <= reset_count - 1'b1;
end else if ((axi_reset_state)&&(reset_count > 0))
reset_count <= reset_count - 1'b1;
else begin
axi_reset_state <= 1'b0;
reset_count <= 4'hf;
end
// Count outstanding transactions
initial pending = 0;
initial outstanding = 0;
always @(posedge i_clk)
if ((i_reset)||(axi_reset_state))
begin
pending <= 0;
outstanding <= 0;
full_fifo <= 0;
end else if ((err_state)||(!i_wb_cyc))
begin
pending <= 0;
outstanding <= 0;
full_fifo <= 0;
end else case({ ((i_wb_stb)&&(!o_wb_stall)), (o_wb_ack) })
2'b01: begin
outstanding <= outstanding - 1'b1;
pending <= (outstanding >= 2);
full_fifo <= 1'b0;
end
2'b10: begin
outstanding <= outstanding + 1'b1;
pending <= 1'b1;
full_fifo <= (outstanding >= {{(LGFIFOLN-2){1'b1}},2'b01});
end
default: begin end
endcase
always @(posedge i_clk)
if ((i_wb_stb)&&(!o_wb_stall))
wb_we <= i_wb_we;
//
//
// Write address logic
//
initial o_axi_awvalid = 0;
always @(posedge i_clk)
if (i_reset)
o_axi_awvalid <= 0;
else
o_axi_awvalid <= (!o_wb_stall)&&(i_wb_stb)&&(i_wb_we)
||(o_axi_awvalid)&&(!i_axi_awready);
always @(posedge i_clk)
if (!o_wb_stall)
o_axi_awaddr <= { i_wb_addr, 2'b00 };
//
//
// Read address logic
//
initial o_axi_arvalid = 1'b0;
always @(posedge i_clk)
if (i_reset)
o_axi_arvalid <= 1'b0;
else
o_axi_arvalid <= (!o_wb_stall)&&(i_wb_stb)&&(!i_wb_we)
||((o_axi_arvalid)&&(!i_axi_arready));
always @(posedge i_clk)
if (!o_wb_stall)
o_axi_araddr <= { i_wb_addr, 2'b00 };
//
//
// Write data logic
//
always @(posedge i_clk)
if (!o_wb_stall)
begin
o_axi_wdata <= i_wb_data;
o_axi_wstrb <= i_wb_sel;
end
initial o_axi_wvalid = 0;
always @(posedge i_clk)
if (i_reset)
o_axi_wvalid <= 0;
else
o_axi_wvalid <= ((!o_wb_stall)&&(i_wb_stb)&&(i_wb_we))
||((o_axi_wvalid)&&(!i_axi_wready));
initial o_wb_ack = 1'b0;
always @(posedge i_clk)
if ((i_reset)||(!i_wb_cyc)||(err_state))
o_wb_ack <= 1'b0;
else if (err_state)
o_wb_ack <= 1'b0;
else if ((i_axi_bvalid)&&(!i_axi_bresp[1]))
o_wb_ack <= 1'b1;
else if ((i_axi_rvalid)&&(!i_axi_rresp[1]))
o_wb_ack <= 1'b1;
else
o_wb_ack <= 1'b0;
always @(posedge i_clk)
o_wb_data <= i_axi_rdata;
// Read data channel / response logic
assign o_axi_rready = 1'b1;
assign o_axi_bready = 1'b1;
initial o_wb_err = 1'b0;
always @(posedge i_clk)
if ((i_reset)||(!i_wb_cyc)||(err_state))
o_wb_err <= 1'b0;
else if ((i_axi_bvalid)&&(i_axi_bresp[1]))
o_wb_err <= 1'b1;
else if ((i_axi_rvalid)&&(i_axi_rresp[1]))
o_wb_err <= 1'b1;
else
o_wb_err <= 1'b0;
initial err_state = 1'b0;
always @(posedge i_clk)
if (i_reset)
err_state <= 0;
else if ((i_axi_bvalid)&&(i_axi_bresp[1]))
err_state <= 1'b1;
else if ((i_axi_rvalid)&&(i_axi_rresp[1]))
err_state <= 1'b1;
else if ((pending)&&(!i_wb_cyc))
err_state <= 1'b1;
else if (err_pending == 0)
err_state <= 0;
initial err_pending = 0;
always @(posedge i_clk)
if (i_reset)
err_pending <= 0;
else case({ ((i_wb_stb)&&(!o_wb_stall)),
((i_axi_bvalid)||(i_axi_rvalid)) })
2'b01: err_pending <= err_pending - 1'b1;
2'b10: err_pending <= err_pending + 1'b1;
default: begin end
endcase
// Make verilator happy
// verilator lint_off UNUSED
wire [2:0] unused;
assign unused = { i_wb_cyc, i_axi_bresp[0], i_axi_rresp[0] };
// verilator lint_on UNUSED
/////////////////////////////////////////////////////////////////////////
//
//
//
// Formal methods section
//
// These are only relevant when *proving* that this translator works
//
//
//
/////////////////////////////////////////////////////////////////////////
`ifdef FORMAL
reg f_past_valid;
//
`define ASSUME assume
`define ASSERT assert
// Parameters
initial assert(DW == 32);
initial assert(C_AXI_ADDR_WIDTH == AW+2);
//
//
// Setup
//
initial f_past_valid = 1'b0;
always @(posedge i_clk)
f_past_valid <= 1'b1;
always @(*)
if (!f_past_valid)
`ASSUME(i_reset);
//////////////////////////////////////////////
//
//
// Assumptions about the WISHBONE inputs
//
//
//////////////////////////////////////////////
always @(*)
assume(f_past_valid || i_reset);
wire [(LGFIFOLN-1):0] f_wb_nreqs, f_wb_nacks,f_wb_outstanding;
fwb_slave #(.DW(DW),.AW(AW),
.F_MAX_STALL(0),
.F_MAX_ACK_DELAY(0),
.F_LGDEPTH(LGFIFOLN),
.F_MAX_REQUESTS(FIFOLN-2))
f_wb(i_clk, i_reset, i_wb_cyc, i_wb_stb, i_wb_we, i_wb_addr,
i_wb_data, i_wb_sel,
o_wb_ack, o_wb_stall, o_wb_data, o_wb_err,
f_wb_nreqs, f_wb_nacks, f_wb_outstanding);
wire [(LGFIFOLN-1):0] f_axi_rd_outstanding,
f_axi_wr_outstanding,
f_axi_awr_outstanding;
faxil_master #(
// .C_AXI_DATA_WIDTH(C_AXI_DATA_WIDTH),
.C_AXI_ADDR_WIDTH(C_AXI_ADDR_WIDTH),
.F_LGDEPTH(LGFIFOLN),
.F_AXI_MAXWAIT(3),
.F_OPT_HAS_CACHE(1'b0),
.F_AXI_MAXDELAY(3))
f_axil(.i_clk(i_clk),
.i_axi_reset_n((!i_reset)&&(!axi_reset_state)),
// Write address channel
.i_axi_awready(i_axi_awready),
.i_axi_awaddr( o_axi_awaddr),
.i_axi_awcache(4'h0),
.i_axi_awprot( o_axi_awprot),
.i_axi_awvalid(o_axi_awvalid),
// Write data channel
.i_axi_wready( i_axi_wready),
.i_axi_wdata( o_axi_wdata),
.i_axi_wstrb( o_axi_wstrb),
.i_axi_wvalid( o_axi_wvalid),
// Write response channel
.i_axi_bresp( i_axi_bresp),
.i_axi_bvalid( i_axi_bvalid),
.i_axi_bready( o_axi_bready),
// Read address channel
.i_axi_arready(i_axi_arready),
.i_axi_araddr( o_axi_araddr),
.i_axi_arcache(4'h0),
.i_axi_arprot( o_axi_arprot),
.i_axi_arvalid(o_axi_arvalid),
// Read data channel
.i_axi_rresp( i_axi_rresp),
.i_axi_rvalid( i_axi_rvalid),
.i_axi_rdata( i_axi_rdata),
.i_axi_rready( o_axi_rready),
// Counts
.f_axi_rd_outstanding( f_axi_rd_outstanding),
.f_axi_wr_outstanding( f_axi_wr_outstanding),
.f_axi_awr_outstanding( f_axi_awr_outstanding)
);
//////////////////////////////////////////////
//
//
// Assumptions about the AXI inputs
//
//
//////////////////////////////////////////////
//////////////////////////////////////////////
//
//
// Assertions about the AXI4 ouputs
//
//
//////////////////////////////////////////////
// Write response channel
always @(posedge i_clk)
// We keep bready high, so the other condition doesn't
// need to be checked
assert(o_axi_bready);
// AXI read data channel signals
always @(posedge i_clk)
// We keep o_axi_rready high, so the other condition's
// don't need to be checked here
assert(o_axi_rready);
//
// Let's look into write requests
//
initial assert(!o_axi_awvalid);
initial assert(!o_axi_wvalid);
always @(posedge i_clk)
if ((!f_past_valid)||($past(i_reset))||($past(axi_reset_state)))
begin
assert(!o_axi_awvalid);
assert(!o_axi_wvalid);
end
always @(posedge i_clk)
if ((f_past_valid)&&(!$past(i_reset))
&&($past((i_wb_stb)&&(i_wb_we)&&(!o_wb_stall))))
begin
// Following any write request that we accept, awvalid
// and wvalid should both be true
assert(o_axi_awvalid);
assert(o_axi_wvalid);
assert(wb_we);
end else if ((f_past_valid)&&($past(i_reset)))
begin
if ($past(i_axi_awready))
assert(!o_axi_awvalid);
if ($past(i_axi_wready))
assert(!o_axi_wvalid);
end
//
// AXI write address channel
//
always @(posedge i_clk)
if ((f_past_valid)&&($past((i_wb_stb)&&(i_wb_we)&&(!o_wb_stall))))
assert(o_axi_awaddr == { $past(i_wb_addr[AW-1:0]), 2'b00 });
//
// AXI write data channel
//
always @(posedge i_clk)
if ((f_past_valid)&&($past(i_wb_stb)&&(i_wb_we)&&(!$past(o_wb_stall))))
begin
assert(o_axi_wdata == $past(i_wb_data));
assert(o_axi_wstrb == $past(i_wb_sel));
end
//
// AXI read address channel
//
initial assert(!o_axi_arvalid);
always @(posedge i_clk)
if ((f_past_valid)&&(!$past(i_reset))
&&($past((i_wb_stb)&&(!i_wb_we)&&(!o_wb_stall))))
begin
assert(o_axi_arvalid);
assert(o_axi_araddr == { $past(i_wb_addr), 2'b00 });
end
//
//
// AXI write response channel
//
//
// AXI read data channel signals
//
always @(posedge i_clk)
if ((f_past_valid)&&(($past(i_reset))||($past(axi_reset_state))))
begin
// Relate err_pending to outstanding
assert(outstanding == 0);
assert(err_pending == 0);
end else if (!err_state)
assert(err_pending == outstanding - ((o_wb_ack)||(o_wb_err)));
always @(posedge i_clk)
if ((f_past_valid)&&(($past(i_reset))||($past(axi_reset_state))))
begin
assert(f_axi_awr_outstanding == 0);
assert(f_axi_wr_outstanding == 0);
assert(f_axi_rd_outstanding == 0);
assert(f_wb_outstanding == 0);
assert(!pending);
assert(outstanding == 0);
assert(err_pending == 0);
end else if (wb_we)
begin
case({o_axi_awvalid,o_axi_wvalid})
2'b00: begin
`ASSERT(f_axi_awr_outstanding == err_pending);
`ASSERT(f_axi_wr_outstanding == err_pending);
end
2'b01: begin
`ASSERT(f_axi_awr_outstanding == err_pending);
`ASSERT(f_axi_wr_outstanding +1 == err_pending);
end
2'b10: begin
`ASSERT(f_axi_awr_outstanding+1 == err_pending);
`ASSERT(f_axi_wr_outstanding == err_pending);
end
2'b11: begin
`ASSERT(f_axi_awr_outstanding+1 == err_pending);
`ASSERT(f_axi_wr_outstanding +1 == err_pending);
end
endcase
//
`ASSERT(!o_axi_arvalid);
`ASSERT(f_axi_rd_outstanding == 0);
end else begin
if (!o_axi_arvalid)
`ASSERT(f_axi_rd_outstanding == err_pending);
else
`ASSERT(f_axi_rd_outstanding+1 == err_pending);
`ASSERT(!o_axi_awvalid);
`ASSERT(!o_axi_wvalid);
`ASSERT(f_axi_awr_outstanding == 0);
`ASSERT(f_axi_wr_outstanding == 0);
end
always @(*)
if ((!i_reset)&&(i_wb_cyc)&&(!err_state))
`ASSERT(f_wb_outstanding == outstanding);
always @(posedge i_clk)
if ((f_past_valid)&&(err_state))
`ASSERT((o_wb_err)||(f_wb_outstanding == 0));
always @(posedge i_clk)
`ASSERT(pending == (outstanding != 0));
//
// Make sure we only create one request at a time
always @(posedge i_clk)
`ASSERT((!o_axi_arvalid)||(!o_axi_wvalid));
always @(posedge i_clk)
`ASSERT((!o_axi_arvalid)||(!o_axi_awvalid));
always @(posedge i_clk)
if (wb_we)
`ASSERT(!o_axi_arvalid);
else
`ASSERT((!o_axi_awvalid)&&(!o_axi_wvalid));
always @(*)
if (&outstanding[LGFIFOLN-1:1])
`ASSERT(full_fifo);
always @(*)
assert(outstanding < {(LGFIFOLN){1'b1}});
// AXI cover results
always @(*)
cover(i_axi_bvalid && o_axi_bready);
always @(*)
cover(i_axi_rvalid && o_axi_rready);
always @(posedge i_clk)
cover(i_axi_bvalid && o_axi_bready
&& $past(i_axi_bvalid && o_axi_bready)
&& $past(i_axi_bvalid && o_axi_bready,2));
always @(posedge i_clk)
cover(i_axi_rvalid && o_axi_rready
&& $past(i_axi_rvalid && o_axi_rready)
&& $past(i_axi_rvalid && o_axi_rready,2));
// AXI cover requests
always @(posedge i_clk)
cover(o_axi_arvalid && i_axi_arready
&& $past(o_axi_arvalid && i_axi_arready)
&& $past(o_axi_arvalid && i_axi_arready,2));
always @(posedge i_clk)
cover(o_axi_awvalid && i_axi_awready
&& $past(o_axi_awvalid && i_axi_awready)
&& $past(o_axi_awvalid && i_axi_awready,2));
always @(posedge i_clk)
cover(o_axi_wvalid && i_axi_wready
&& $past(o_axi_wvalid && i_axi_wready)
&& $past(o_axi_wvalid && i_axi_wready,2));
always @(*)
cover(i_axi_rvalid && o_axi_rready);
// Wishbone cover results
always @(*)
cover(i_wb_cyc && o_wb_ack);
always @(posedge i_clk)
cover(i_wb_cyc && o_wb_ack
&& $past(o_wb_ack)&&$past(o_wb_ack,2));
`endif
endmodule
`ifndef YOSYS
`default_nettype wire
`endif