added AXI4 feature
This commit is contained in:
parent
18283f4436
commit
66f0daf0e9
|
|
@ -0,0 +1,235 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Filename: axi_addr.v
|
||||
// {{{
|
||||
// Project: WB2AXIPSP: bus bridges and other odds and ends
|
||||
//
|
||||
// Purpose: The AXI (full) standard has some rather complicated addressing
|
||||
// modes, where the address can either be FIXED, INCRementing, or
|
||||
// even where it can WRAP around some boundary. When in either INCR or
|
||||
// WRAP modes, the next address must always be aligned. In WRAP mode,
|
||||
// the next address calculation needs to wrap around a given value, and
|
||||
// that value is dependent upon the burst size (i.e. bytes per beat) and
|
||||
// length (total numbers of beats). Since this calculation can be
|
||||
// non-trivial, and since it needs to be done multiple times, the logic
|
||||
// below captures it for every time it might be needed.
|
||||
//
|
||||
// 20200918 - modified to accommodate (potential) AXI3 burst lengths
|
||||
//
|
||||
// Creator: Dan Gisselquist, Ph.D.
|
||||
// Gisselquist Technology, LLC
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// }}}
|
||||
// Copyright (C) 2019-2024, 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 axi_addr #(
|
||||
// {{{
|
||||
parameter AW = 32,
|
||||
DW = 32,
|
||||
// parameter [0:0] OPT_AXI3 = 1'b0,
|
||||
localparam LENB = 8
|
||||
// }}}
|
||||
) (
|
||||
// {{{
|
||||
input wire [AW-1:0] i_last_addr,
|
||||
input wire [2:0] i_size, // 1b, 2b, 4b, 8b, etc
|
||||
input wire [1:0] i_burst, // fixed, incr, wrap, reserved
|
||||
input wire [LENB-1:0] i_len,
|
||||
output wire [AW-1:0] o_next_addr
|
||||
// }}}
|
||||
);
|
||||
|
||||
// Parameter/register declarations
|
||||
// {{{
|
||||
localparam DSZ = $clog2(DW)-3;
|
||||
localparam [1:0] FIXED = 2'b00;
|
||||
// localparam [1:0] INCREMENT = 2'b01;
|
||||
// localparam [1:0] WRAP = 2'b10;
|
||||
localparam IN_AW = (AW >= 12) ? 12 : AW;
|
||||
localparam [IN_AW-1:0] ONE = 1;
|
||||
|
||||
reg [IN_AW-1:0] wrap_mask, increment;
|
||||
reg [IN_AW-1:0] crossblk_addr, aligned_addr, unaligned_addr;
|
||||
// }}}
|
||||
|
||||
// Address increment
|
||||
// {{{
|
||||
always @(*)
|
||||
if (DSZ == 0)
|
||||
increment = 1;
|
||||
else if (DSZ == 1)
|
||||
increment = (i_size[0]) ? 2 : 1;
|
||||
else if (DSZ == 2)
|
||||
increment = (i_size[1]) ? 4 : ((i_size[0]) ? 2 : 1);
|
||||
else if (DSZ == 3)
|
||||
case(i_size[1:0])
|
||||
2'b00: increment = 1;
|
||||
2'b01: increment = 2;
|
||||
2'b10: increment = 4;
|
||||
2'b11: increment = 8;
|
||||
endcase
|
||||
else
|
||||
increment = (1<<i_size);
|
||||
// }}}
|
||||
|
||||
// wrap_mask
|
||||
// {{{
|
||||
// The wrap_mask is used to determine which bits remain stable across
|
||||
// the burst, and which are allowed to change. It is only used during
|
||||
// wrapped addressing.
|
||||
always @(*)
|
||||
begin
|
||||
// Start with the default, minimum mask
|
||||
|
||||
/*
|
||||
// Here's the original code. It works, but it's
|
||||
// not economical (uses too many LUTs)
|
||||
//
|
||||
if (i_len[3:0] == 1)
|
||||
wrap_mask = (1<<(i_size+1));
|
||||
else if (i_len[3:0] == 3)
|
||||
wrap_mask = (1<<(i_size+2));
|
||||
else if (i_len[3:0] == 7)
|
||||
wrap_mask = (1<<(i_size+3));
|
||||
else if (i_len[3:0] == 15)
|
||||
wrap_mask = (1<<(i_size+4));
|
||||
wrap_mask = wrap_mask - 1;
|
||||
*/
|
||||
|
||||
// Here's what we *want*
|
||||
//
|
||||
// wrap_mask[i_size:0] = -1;
|
||||
//
|
||||
// On the other hand, since we're already guaranteed that our
|
||||
// addresses are aligned, do we really care about
|
||||
// wrap_mask[i_size-1:0] ?
|
||||
|
||||
// What we want:
|
||||
//
|
||||
// wrap_mask[i_size+3:i_size] |= i_len[3:0]
|
||||
//
|
||||
// We could simplify this to
|
||||
//
|
||||
// wrap_mask = wrap_mask | (i_len[3:0] << (i_size));
|
||||
// Verilator lint_off WIDTH
|
||||
if (DSZ < 2)
|
||||
wrap_mask = ONE | ({{(IN_AW-4){1'b0}},i_len[3:0]} << (i_size[0]));
|
||||
else if (DSZ < 4)
|
||||
wrap_mask = ONE | ({{(IN_AW-4){1'b0}},i_len[3:0]} << (i_size[1:0]));
|
||||
else
|
||||
wrap_mask = ONE | ({{(IN_AW-4){1'b0}},i_len[3:0]} << (i_size));
|
||||
// Verilator lint_on WIDTH
|
||||
end
|
||||
// }}}
|
||||
|
||||
// unaligned_addr
|
||||
always @(*)
|
||||
unaligned_addr = i_last_addr[IN_AW-1:0] + increment[IN_AW-1:0];
|
||||
|
||||
// aligned_addr
|
||||
// {{{
|
||||
always @(*)
|
||||
if (i_burst != FIXED)
|
||||
begin
|
||||
// Align subsequent beats in any burst
|
||||
// {{{
|
||||
aligned_addr = unaligned_addr;
|
||||
// We use the bus size here to simplify the logic
|
||||
// required in case the bus is smaller than the
|
||||
// maximum. This depends upon AxSIZE being less than
|
||||
// $clog2(DATA_WIDTH/8).
|
||||
if (DSZ < 2)
|
||||
begin
|
||||
// {{{
|
||||
// Align any subsequent address
|
||||
if (i_size[0])
|
||||
aligned_addr[0] = 0;
|
||||
// }}}
|
||||
end else if (DSZ < 4)
|
||||
begin
|
||||
// {{{
|
||||
// Align any subsequent address
|
||||
case(i_size[1:0])
|
||||
2'b00: aligned_addr = unaligned_addr;
|
||||
2'b01: aligned_addr[ 0] = 0;
|
||||
2'b10: aligned_addr[(AW-1>1) ? 1 : (AW-1):0]= 0;
|
||||
2'b11: aligned_addr[(AW-1>2) ? 2 : (AW-1):0]= 0;
|
||||
endcase
|
||||
// }}}
|
||||
end else begin
|
||||
// {{{
|
||||
// Align any subsequent address
|
||||
case(i_size)
|
||||
3'b001: aligned_addr[ 0] = 0;
|
||||
3'b010: aligned_addr[(AW-1>1) ? 1 : (AW-1):0]=0;
|
||||
3'b011: aligned_addr[(AW-1>2) ? 2 : (AW-1):0]=0;
|
||||
3'b100: aligned_addr[(AW-1>3) ? 3 : (AW-1):0]=0;
|
||||
3'b101: aligned_addr[(AW-1>4) ? 4 : (AW-1):0]=0;
|
||||
3'b110: aligned_addr[(AW-1>5) ? 5 : (AW-1):0]=0;
|
||||
3'b111: aligned_addr[(AW-1>6) ? 6 : (AW-1):0]=0;
|
||||
default: aligned_addr = unaligned_addr;
|
||||
endcase
|
||||
// }}}
|
||||
end
|
||||
// }}}
|
||||
end else
|
||||
aligned_addr = i_last_addr[IN_AW-1:0];
|
||||
// }}}
|
||||
|
||||
// crossblk_addr from aligned_addr, for WRAP addressing
|
||||
// {{{
|
||||
always @(*)
|
||||
if (i_burst[1])
|
||||
begin
|
||||
// WRAP!
|
||||
crossblk_addr[IN_AW-1:0] = (i_last_addr[IN_AW-1:0] & ~wrap_mask)
|
||||
| (aligned_addr & wrap_mask);
|
||||
end else
|
||||
crossblk_addr[IN_AW-1:0] = aligned_addr;
|
||||
// }}}
|
||||
|
||||
// o_next_addr: Guarantee only the bottom 12 bits change
|
||||
// {{{
|
||||
// This is really a logic simplification. AXI bursts aren't allowed
|
||||
// to cross 4kB boundaries. Given that's the case, we don't have to
|
||||
// suffer from the propagation across all AW bits, and can limit any
|
||||
// address propagation to just the lower 12 bits
|
||||
generate if (AW > 12)
|
||||
begin : WIDE_ADDRESS
|
||||
assign o_next_addr = { i_last_addr[AW-1:12],
|
||||
crossblk_addr[11:0] };
|
||||
end else begin : NARROW_ADDRESS
|
||||
assign o_next_addr = crossblk_addr[AW-1:0];
|
||||
end endgenerate
|
||||
// }}}
|
||||
|
||||
// Make Verilator happy
|
||||
// {{{
|
||||
// Verilator lint_off UNUSED
|
||||
wire unused;
|
||||
assign unused = (LENB <= 4) ? &{1'b0, i_len[0] }
|
||||
: &{ 1'b0, i_len[LENB-1:4], i_len[0] };
|
||||
// Verilator lint_on UNUSED
|
||||
// }}}
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,317 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Filename: axim2wbsp.v
|
||||
// {{{
|
||||
// Project: WB2AXIPSP: bus bridges and other odds and ends
|
||||
//
|
||||
// Purpose: So ... this converter works in the other direction from
|
||||
// wbm2axisp. This converter takes AXI commands, and organizes
|
||||
// them into pipelined wishbone commands.
|
||||
//
|
||||
// This particular core treats AXI as two separate buses: one for writes,
|
||||
// and the other for reads. This particular core combines the two channels
|
||||
// into one. The designer should be aware that the two AXI buses turned
|
||||
// Wishbone buses can be kept separate as separate inputs to a WB crosssbar
|
||||
// for better performance in some circumstances.
|
||||
//
|
||||
// Creator: Dan Gisselquist, Ph.D.
|
||||
// Gisselquist Technology, LLC
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// }}}
|
||||
// Copyright (C) 2016-2024, 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 axim2wbsp #(
|
||||
// {{{
|
||||
parameter C_AXI_ID_WIDTH = 2, // 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
|
||||
localparam AXI_LSBS = $clog2(C_AXI_DATA_WIDTH)-3,
|
||||
localparam DW = C_AXI_DATA_WIDTH,
|
||||
localparam AW = C_AXI_ADDR_WIDTH - AXI_LSBS,
|
||||
parameter LGFIFO = 5,
|
||||
parameter [0:0] OPT_SWAP_ENDIANNESS = 1'b0,
|
||||
parameter [0:0] OPT_READONLY = 1'b0,
|
||||
parameter [0:0] OPT_WRITEONLY = 1'b0
|
||||
// }}}
|
||||
) (
|
||||
// {{{
|
||||
//
|
||||
input wire S_AXI_ACLK, // System clock
|
||||
input wire S_AXI_ARESETN,
|
||||
|
||||
// AXI write address channel signals
|
||||
// {{{
|
||||
input wire S_AXI_AWVALID,
|
||||
output wire S_AXI_AWREADY,
|
||||
input wire [C_AXI_ID_WIDTH-1:0] S_AXI_AWID,
|
||||
input wire [C_AXI_ADDR_WIDTH-1:0] S_AXI_AWADDR,
|
||||
input wire [7:0] S_AXI_AWLEN,
|
||||
input wire [2:0] S_AXI_AWSIZE,
|
||||
input wire [1:0] S_AXI_AWBURST,
|
||||
input wire [0:0] S_AXI_AWLOCK,
|
||||
input wire [3:0] S_AXI_AWCACHE,
|
||||
input wire [2:0] S_AXI_AWPROT,
|
||||
input wire [3:0] S_AXI_AWQOS,
|
||||
// }}}
|
||||
// AXI write data channel signals
|
||||
// {{{
|
||||
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,
|
||||
input wire S_AXI_WLAST,
|
||||
// }}}
|
||||
// AXI write response channel signals
|
||||
// {{{
|
||||
output wire S_AXI_BVALID,
|
||||
input wire S_AXI_BREADY,
|
||||
output wire [C_AXI_ID_WIDTH-1:0] S_AXI_BID,
|
||||
output wire [1:0] S_AXI_BRESP,
|
||||
// }}}
|
||||
// AXI read address channel signals
|
||||
// {{{
|
||||
input wire S_AXI_ARVALID,
|
||||
output wire S_AXI_ARREADY,
|
||||
input wire [C_AXI_ID_WIDTH-1:0] S_AXI_ARID,
|
||||
input wire [C_AXI_ADDR_WIDTH-1:0] S_AXI_ARADDR,
|
||||
input wire [7:0] S_AXI_ARLEN,
|
||||
input wire [2:0] S_AXI_ARSIZE,
|
||||
input wire [1:0] S_AXI_ARBURST,
|
||||
input wire [0:0] S_AXI_ARLOCK,
|
||||
input wire [3:0] S_AXI_ARCACHE,
|
||||
input wire [2:0] S_AXI_ARPROT,
|
||||
input wire [3:0] S_AXI_ARQOS,
|
||||
// }}}
|
||||
// AXI read data channel signals
|
||||
// {{{
|
||||
output wire S_AXI_RVALID, // Rd rslt valid
|
||||
input wire S_AXI_RREADY, // Rd rslt 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 wire [1:0] S_AXI_RRESP, // Read response
|
||||
// }}}
|
||||
// We'll share the clock and the reset
|
||||
// {{{
|
||||
output wire o_reset,
|
||||
output wire o_wb_cyc,
|
||||
output wire o_wb_stb,
|
||||
output wire o_wb_we,
|
||||
output wire [(AW-1):0] o_wb_addr,
|
||||
output wire [(C_AXI_DATA_WIDTH-1):0] o_wb_data,
|
||||
output wire [(C_AXI_DATA_WIDTH/8-1):0] o_wb_sel,
|
||||
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
|
||||
// }}}
|
||||
// }}}
|
||||
);
|
||||
//
|
||||
//
|
||||
//
|
||||
|
||||
|
||||
wire [(AW-1):0] w_wb_addr, r_wb_addr;
|
||||
wire [(C_AXI_DATA_WIDTH-1):0] w_wb_data;
|
||||
wire [(C_AXI_DATA_WIDTH/8-1):0] w_wb_sel, r_wb_sel;
|
||||
wire r_wb_err, r_wb_cyc, r_wb_stb, r_wb_stall, r_wb_ack;
|
||||
wire w_wb_err, w_wb_cyc, w_wb_stb, w_wb_stall, w_wb_ack;
|
||||
wire r_wb_we, w_wb_we;
|
||||
|
||||
assign r_wb_we = 1'b0;
|
||||
assign w_wb_we = 1'b1;
|
||||
|
||||
generate if (!OPT_READONLY)
|
||||
begin : AXI_WR
|
||||
// {{{
|
||||
aximwr2wbsp #(
|
||||
// {{{
|
||||
.C_AXI_ID_WIDTH(C_AXI_ID_WIDTH),
|
||||
.C_AXI_DATA_WIDTH(C_AXI_DATA_WIDTH),
|
||||
.C_AXI_ADDR_WIDTH(C_AXI_ADDR_WIDTH),
|
||||
.OPT_SWAP_ENDIANNESS(OPT_SWAP_ENDIANNESS),
|
||||
.LGFIFO(LGFIFO)
|
||||
// }}}
|
||||
) axi_write_decoder(
|
||||
// {{{
|
||||
.S_AXI_ACLK(S_AXI_ACLK), .S_AXI_ARESETN(S_AXI_ARESETN),
|
||||
//
|
||||
.S_AXI_AWVALID(S_AXI_AWVALID),
|
||||
.S_AXI_AWREADY(S_AXI_AWREADY),
|
||||
.S_AXI_AWID( S_AXI_AWID),
|
||||
.S_AXI_AWADDR( S_AXI_AWADDR),
|
||||
.S_AXI_AWLEN( S_AXI_AWLEN),
|
||||
.S_AXI_AWSIZE( S_AXI_AWSIZE),
|
||||
.S_AXI_AWBURST(S_AXI_AWBURST),
|
||||
.S_AXI_AWLOCK( S_AXI_AWLOCK),
|
||||
.S_AXI_AWCACHE(S_AXI_AWCACHE),
|
||||
.S_AXI_AWPROT( S_AXI_AWPROT),
|
||||
.S_AXI_AWQOS( S_AXI_AWQOS),
|
||||
//
|
||||
.S_AXI_WVALID( S_AXI_WVALID),
|
||||
.S_AXI_WREADY( S_AXI_WREADY),
|
||||
.S_AXI_WDATA( S_AXI_WDATA),
|
||||
.S_AXI_WSTRB( S_AXI_WSTRB),
|
||||
.S_AXI_WLAST( S_AXI_WLAST),
|
||||
//
|
||||
.S_AXI_BVALID(S_AXI_BVALID),
|
||||
.S_AXI_BREADY(S_AXI_BREADY),
|
||||
.S_AXI_BID( S_AXI_BID),
|
||||
.S_AXI_BRESP( S_AXI_BRESP),
|
||||
//
|
||||
.o_wb_cyc( w_wb_cyc),
|
||||
.o_wb_stb( w_wb_stb),
|
||||
.o_wb_addr( w_wb_addr),
|
||||
.o_wb_data( w_wb_data),
|
||||
.o_wb_sel( w_wb_sel),
|
||||
.i_wb_ack( w_wb_ack),
|
||||
.i_wb_stall(w_wb_stall),
|
||||
.i_wb_err( w_wb_err)
|
||||
// }}}
|
||||
);
|
||||
// }}}
|
||||
end else begin : NO_WRITE_CHANNEL
|
||||
// {{{
|
||||
assign w_wb_cyc = 0;
|
||||
assign w_wb_stb = 0;
|
||||
assign w_wb_addr = 0;
|
||||
assign w_wb_data = 0;
|
||||
assign w_wb_sel = 0;
|
||||
assign S_AXI_AWREADY = 0;
|
||||
assign S_AXI_WREADY = 0;
|
||||
assign S_AXI_BVALID = 0;
|
||||
assign S_AXI_BRESP = 2'b11;
|
||||
assign S_AXI_BID = 0;
|
||||
// }}}
|
||||
end endgenerate
|
||||
|
||||
generate if (!OPT_WRITEONLY)
|
||||
begin : AXI_RD
|
||||
// {{{
|
||||
aximrd2wbsp #(
|
||||
// {{{
|
||||
.C_AXI_ID_WIDTH(C_AXI_ID_WIDTH),
|
||||
.C_AXI_DATA_WIDTH(C_AXI_DATA_WIDTH),
|
||||
.C_AXI_ADDR_WIDTH(C_AXI_ADDR_WIDTH),
|
||||
.OPT_SWAP_ENDIANNESS(OPT_SWAP_ENDIANNESS),
|
||||
.LGFIFO(LGFIFO)
|
||||
// }}}
|
||||
) axi_read_decoder(
|
||||
// {{{
|
||||
.S_AXI_ACLK(S_AXI_ACLK), .S_AXI_ARESETN(S_AXI_ARESETN),
|
||||
//
|
||||
.S_AXI_ARVALID(S_AXI_ARVALID),
|
||||
.S_AXI_ARREADY(S_AXI_ARREADY),
|
||||
.S_AXI_ARID( S_AXI_ARID),
|
||||
.S_AXI_ARADDR( S_AXI_ARADDR),
|
||||
.S_AXI_ARLEN( S_AXI_ARLEN),
|
||||
.S_AXI_ARSIZE( S_AXI_ARSIZE),
|
||||
.S_AXI_ARBURST(S_AXI_ARBURST),
|
||||
.S_AXI_ARLOCK( S_AXI_ARLOCK),
|
||||
.S_AXI_ARCACHE(S_AXI_ARCACHE),
|
||||
.S_AXI_ARPROT( S_AXI_ARPROT),
|
||||
.S_AXI_ARQOS( S_AXI_ARQOS),
|
||||
//
|
||||
.S_AXI_RVALID(S_AXI_RVALID),
|
||||
.S_AXI_RREADY(S_AXI_RREADY),
|
||||
.S_AXI_RID( S_AXI_RID),
|
||||
.S_AXI_RDATA( S_AXI_RDATA),
|
||||
.S_AXI_RLAST( S_AXI_RLAST),
|
||||
.S_AXI_RRESP( S_AXI_RRESP),
|
||||
//
|
||||
.o_wb_cyc( r_wb_cyc),
|
||||
.o_wb_stb( r_wb_stb),
|
||||
.o_wb_addr( r_wb_addr),
|
||||
.o_wb_sel( r_wb_sel),
|
||||
.i_wb_ack( r_wb_ack),
|
||||
.i_wb_stall(r_wb_stall),
|
||||
.i_wb_data( i_wb_data),
|
||||
.i_wb_err( r_wb_err)
|
||||
// }}}
|
||||
);
|
||||
// }}}
|
||||
end else begin : NO_READ_CHANNEL
|
||||
// {{{
|
||||
assign r_wb_cyc = 0;
|
||||
assign r_wb_stb = 0;
|
||||
assign r_wb_addr = 0;
|
||||
//
|
||||
assign S_AXI_ARREADY = 0;
|
||||
assign S_AXI_RVALID = 0;
|
||||
assign S_AXI_RID = 0;
|
||||
assign S_AXI_RDATA = 0;
|
||||
assign S_AXI_RLAST = 0;
|
||||
assign S_AXI_RRESP = 0;
|
||||
// }}}
|
||||
end endgenerate
|
||||
|
||||
generate if (OPT_READONLY)
|
||||
begin : ARB_RD
|
||||
// {{{
|
||||
assign o_wb_cyc = r_wb_cyc;
|
||||
assign o_wb_stb = r_wb_stb;
|
||||
assign o_wb_we = r_wb_we;
|
||||
assign o_wb_addr = r_wb_addr;
|
||||
assign o_wb_data = 0;
|
||||
assign o_wb_sel = r_wb_sel;
|
||||
assign r_wb_ack = i_wb_ack;
|
||||
assign r_wb_stall= i_wb_stall;
|
||||
assign r_wb_ack = i_wb_ack;
|
||||
assign r_wb_err = i_wb_err;
|
||||
// }}}
|
||||
end else if (OPT_WRITEONLY)
|
||||
begin : ARB_WR
|
||||
// {{{
|
||||
assign o_wb_cyc = w_wb_cyc;
|
||||
assign o_wb_stb = w_wb_stb;
|
||||
assign o_wb_we = w_wb_we;
|
||||
assign o_wb_addr = w_wb_addr;
|
||||
assign o_wb_data = w_wb_data;
|
||||
assign o_wb_sel = w_wb_sel;
|
||||
assign w_wb_ack = i_wb_ack;
|
||||
assign w_wb_stall= i_wb_stall;
|
||||
assign w_wb_ack = i_wb_ack;
|
||||
assign w_wb_err = i_wb_err;
|
||||
// }}}
|
||||
end else begin : ARB_WB
|
||||
// {{{
|
||||
wbarbiter #(.DW(DW), .AW(AW))
|
||||
readorwrite(S_AXI_ACLK, o_reset,
|
||||
r_wb_cyc, r_wb_stb, r_wb_we, r_wb_addr, w_wb_data, r_wb_sel,
|
||||
r_wb_ack, r_wb_stall, r_wb_err,
|
||||
w_wb_cyc, w_wb_stb, w_wb_we, w_wb_addr, w_wb_data, w_wb_sel,
|
||||
w_wb_ack, w_wb_stall, w_wb_err,
|
||||
o_wb_cyc, o_wb_stb, o_wb_we, o_wb_addr, o_wb_data, o_wb_sel,
|
||||
i_wb_ack, i_wb_stall, i_wb_err
|
||||
);
|
||||
// }}}
|
||||
end endgenerate
|
||||
|
||||
assign o_reset = (S_AXI_ARESETN == 1'b0);
|
||||
|
||||
`ifdef FORMAL
|
||||
`endif
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,740 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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-2024, 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
|
||||
localparam AXI_LSBS = $clog2(C_AXI_DATA_WIDTH/8),
|
||||
localparam AW = C_AXI_ADDR_WIDTH - AXI_LSBS,
|
||||
parameter LGFIFO = 3,
|
||||
parameter [0:0] OPT_SWAP_ENDIANNESS = 1'b0,
|
||||
parameter [0:0] OPT_SIZESEL = 1
|
||||
// parameter WBMODE = "B4PIPELINE"
|
||||
// Could also be "BLOCK"
|
||||
// }}}
|
||||
) (
|
||||
// {{{
|
||||
input wire S_AXI_ACLK, // Bus clock
|
||||
input wire S_AXI_ARESETN, // Bus reset
|
||||
// AXI
|
||||
// {{{
|
||||
// AXI read address channel signals
|
||||
input wire S_AXI_ARVALID,
|
||||
output wire S_AXI_ARREADY,
|
||||
input wire [C_AXI_ID_WIDTH-1:0] S_AXI_ARID,
|
||||
input wire [C_AXI_ADDR_WIDTH-1:0] S_AXI_ARADDR,
|
||||
input wire [7:0] S_AXI_ARLEN,
|
||||
input wire [2:0] S_AXI_ARSIZE,
|
||||
input wire [1:0] S_AXI_ARBURST,
|
||||
input wire [0:0] S_AXI_ARLOCK,
|
||||
input wire [3:0] S_AXI_ARCACHE,
|
||||
input wire [2:0] S_AXI_ARPROT,
|
||||
input wire [3:0] S_AXI_ARQOS,
|
||||
|
||||
// AXI read data channel signals
|
||||
output reg S_AXI_RVALID,
|
||||
input wire S_AXI_RREADY,
|
||||
output wire [C_AXI_ID_WIDTH-1:0] S_AXI_RID,
|
||||
output wire [C_AXI_DATA_WIDTH-1:0] S_AXI_RDATA,
|
||||
output wire S_AXI_RLAST,
|
||||
output reg [1:0] S_AXI_RRESP,
|
||||
// }}}
|
||||
// Wishbone channel
|
||||
// {{{
|
||||
// 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 wire [(C_AXI_DATA_WIDTH/8-1):0] o_wb_sel,
|
||||
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
|
||||
// }}}
|
||||
// }}}
|
||||
);
|
||||
|
||||
// Register/net definitions
|
||||
// {{{
|
||||
wire w_reset;
|
||||
|
||||
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 [C_AXI_DATA_WIDTH/8-1:0] axi_strb;
|
||||
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;
|
||||
|
||||
reg [C_AXI_DATA_WIDTH-1:0] read_data;
|
||||
|
||||
assign w_reset = (S_AXI_ARESETN == 1'b0);
|
||||
// }}}
|
||||
|
||||
// incoming skidbuffer
|
||||
// {{{
|
||||
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 });
|
||||
// }}}
|
||||
|
||||
// accept_request
|
||||
// {{{
|
||||
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));
|
||||
// }}}
|
||||
|
||||
// o_wb_cyc, o_wb_stb, stblen, last_stb
|
||||
// {{{
|
||||
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
|
||||
// }}}
|
||||
|
||||
// stblen, last_stb, midissue
|
||||
// {{{
|
||||
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
|
||||
// }}}
|
||||
|
||||
// axi_id, axi_burst, axi_size, axi_len
|
||||
// {{{
|
||||
initial axi_size = AXI_LSBS[2:0];
|
||||
initial axi_strb = -1;
|
||||
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;
|
||||
|
||||
if (OPT_SIZESEL)
|
||||
axi_strb <= (1<<(1<<(skid_arsize)))-1;
|
||||
else
|
||||
axi_strb <= { (C_AXI_DATA_WIDTH/8){1'b1} };
|
||||
end
|
||||
`ifdef FORMAL
|
||||
always @(*)
|
||||
case(axi_size)
|
||||
0: assert(axi_strb == 1);
|
||||
1: assert((C_AXI_DATA_WIDTH > 8) && (axi_strb == 2'b11));
|
||||
2: assert((C_AXI_DATA_WIDTH > 16) && (axi_strb == 4'b1111));
|
||||
3: assert((C_AXI_DATA_WIDTH > 32) && (axi_strb == 8'hff));
|
||||
4: assert((C_AXI_DATA_WIDTH > 64) && (axi_strb == 16'hffff));
|
||||
5: assert((C_AXI_DATA_WIDTH > 128) && (axi_strb == 32'hffff_ffff));
|
||||
6: assert((C_AXI_DATA_WIDTH > 256) && (axi_strb == 64'hffff_ffff_ffff_ffff));
|
||||
default: assert((C_AXI_DATA_WIDTH == 1024) && (&axi_strb));
|
||||
endcase
|
||||
`endif
|
||||
// }}}
|
||||
|
||||
// next_addr
|
||||
// {{{
|
||||
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];
|
||||
|
||||
// o_wb_sel
|
||||
// {{{
|
||||
generate if (OPT_SIZESEL && C_AXI_DATA_WIDTH > 8)
|
||||
begin : MULTI_BYTE_SEL
|
||||
// {{{
|
||||
assign o_wb_sel = axi_strb << axi_addr[AXI_LSBS-1:0];
|
||||
// }}}
|
||||
end else begin : FULL_WORD_SEL
|
||||
assign o_wb_sel = {(C_AXI_DATA_WIDTH/8){1'b1}};
|
||||
|
||||
// Verilator lint_off UNUSED
|
||||
wire unused_sel;
|
||||
assign unused_sel = &{ 1'b0, axi_strb };
|
||||
// Verilator lint_on UNUSED
|
||||
end endgenerate
|
||||
// }}}
|
||||
|
||||
// lastid_fifo
|
||||
// {{{
|
||||
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);
|
||||
// }}}
|
||||
|
||||
// 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
|
||||
// }}}
|
||||
|
||||
// resp_fifo
|
||||
// {{{
|
||||
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);
|
||||
// }}}
|
||||
|
||||
// acks_expected
|
||||
// {{{
|
||||
// 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
|
||||
// {{{
|
||||
// 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
|
||||
// }}}
|
||||
|
||||
// fifo_nearfull
|
||||
// {{{
|
||||
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
|
||||
// }}}
|
||||
|
||||
// wb_empty
|
||||
// {{{
|
||||
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
|
||||
// }}}
|
||||
|
||||
// S_AXI_RRESP, S_AXI_RVALID
|
||||
// {{{
|
||||
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
|
||||
// }}}
|
||||
|
||||
// err_state
|
||||
// {{{
|
||||
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
|
||||
// }}}
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Formal properties
|
||||
// {{{
|
||||
//
|
||||
// The following are the formal properties used to verify this core.
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
`ifdef FORMAL
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// The following are a subset of the properties used to verify this
|
||||
// core.
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
//
|
||||
localparam DW = C_AXI_DATA_WIDTH;
|
||||
|
||||
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
|
||||
|
||||
// wb_empty
|
||||
// {{{
|
||||
always @(*)
|
||||
if (o_wb_cyc)
|
||||
assert(wb_empty == (lastid_fifo_fill == resp_fifo_fill));
|
||||
// }}}
|
||||
|
||||
// !o_wb_cyc, if nothing pending
|
||||
// {{{
|
||||
always @(*)
|
||||
if ((fwb_nacks == fwb_nreqs)&&(!midissue))
|
||||
assert(!o_wb_cyc);
|
||||
// }}}
|
||||
|
||||
always @(*)
|
||||
assert(fwb_outstanding <= (1<<LGFIFO));
|
||||
|
||||
// fifo_nearfull
|
||||
// {{{
|
||||
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;
|
||||
|
||||
// cvr_reads
|
||||
// {{{
|
||||
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;
|
||||
// }}}
|
||||
|
||||
// cvr_read_bursts
|
||||
// {{{
|
||||
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;
|
||||
// }}}
|
||||
|
||||
// cvr_read_id
|
||||
// {{{
|
||||
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;
|
||||
// }}}
|
||||
|
||||
// cvr_rdid_bursts
|
||||
// {{{
|
||||
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
|
||||
|
|
@ -0,0 +1,751 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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-2024, 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,
|
||||
parameter C_AXI_DATA_WIDTH = 32,
|
||||
parameter C_AXI_ADDR_WIDTH = 28,
|
||||
parameter [0:0] OPT_SWAP_ENDIANNESS = 1'b0,
|
||||
localparam AXI_LSBS = $clog2(C_AXI_DATA_WIDTH)-3,
|
||||
localparam AW = C_AXI_ADDR_WIDTH-AXI_LSBS,
|
||||
|
||||
parameter LGFIFO = 5
|
||||
// }}}
|
||||
) (
|
||||
// {{{
|
||||
input wire S_AXI_ACLK, // System clock
|
||||
input wire S_AXI_ARESETN,
|
||||
// Incoming AXI bus connections
|
||||
// {{{
|
||||
// AXI write address channel signals
|
||||
input wire S_AXI_AWVALID,
|
||||
output wire S_AXI_AWREADY,
|
||||
input wire [C_AXI_ID_WIDTH-1:0] S_AXI_AWID,
|
||||
input wire [C_AXI_ADDR_WIDTH-1:0] S_AXI_AWADDR,
|
||||
input wire [7:0] S_AXI_AWLEN,
|
||||
input wire [2:0] S_AXI_AWSIZE,
|
||||
input wire [1:0] S_AXI_AWBURST,
|
||||
input wire [0:0] S_AXI_AWLOCK,
|
||||
input wire [3:0] S_AXI_AWCACHE,
|
||||
input wire [2:0] S_AXI_AWPROT,
|
||||
input wire [3:0] S_AXI_AWQOS,
|
||||
|
||||
// AXI write data channel signals
|
||||
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,
|
||||
input wire S_AXI_WLAST,
|
||||
|
||||
// AXI write response channel signals
|
||||
output wire S_AXI_BVALID,
|
||||
input wire S_AXI_BREADY,
|
||||
output wire [C_AXI_ID_WIDTH-1:0] S_AXI_BID,
|
||||
output wire [1:0] S_AXI_BRESP,
|
||||
// }}}
|
||||
// Downstream wishbone bus
|
||||
// {{{
|
||||
// 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
|
||||
// }}}
|
||||
|
||||
// }}}
|
||||
);
|
||||
|
||||
// Register/net declarations
|
||||
// {{{
|
||||
localparam DW = C_AXI_DATA_WIDTH;
|
||||
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;
|
||||
|
||||
reg [8:0] next_acklen;
|
||||
reg [1:0] next_acklow;
|
||||
|
||||
assign w_reset = (S_AXI_ARESETN == 1'b0);
|
||||
// }}}
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Skid buffers--all incoming signals go throug skid buffers
|
||||
// {{{
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
//
|
||||
|
||||
// write address skid buffer
|
||||
// {{{
|
||||
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 });
|
||||
// }}}
|
||||
|
||||
// write channel skid buffer
|
||||
// {{{
|
||||
skidbuffer #(
|
||||
`ifdef FORMAL
|
||||
.OPT_PASSTHROUGH(1'b1),
|
||||
`endif
|
||||
.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 });
|
||||
// }}}
|
||||
|
||||
// accept_write_burst
|
||||
// {{{
|
||||
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
|
||||
// }}}
|
||||
|
||||
// skid_wready
|
||||
// {{{
|
||||
always @(*)
|
||||
skid_wready = (!o_wb_stb || !i_wb_stall || err_state)
|
||||
&&(!skid_awready || accept_write_burst);
|
||||
// }}}
|
||||
|
||||
// skid_awready
|
||||
// {{{
|
||||
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;
|
||||
// }}}
|
||||
|
||||
// }}}
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Burst unwinding
|
||||
// {{{
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
//
|
||||
|
||||
// axi_w*, wlen -- properties of the currently active burst
|
||||
// {{{
|
||||
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
|
||||
// }}}
|
||||
|
||||
// next_addr
|
||||
// {{{
|
||||
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);
|
||||
// }}}
|
||||
|
||||
// }}}
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Issue the Wishbone request
|
||||
// {{{
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
//
|
||||
|
||||
// o_wb_cyc, o_wb_stb
|
||||
// {{{
|
||||
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];
|
||||
|
||||
// o_wb_data, o_wb_sel
|
||||
// {{{
|
||||
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
|
||||
// }}}
|
||||
|
||||
// }}}
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// FIFO usage tracking
|
||||
// {{{
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
//
|
||||
|
||||
// writes_expected
|
||||
// {{{
|
||||
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
|
||||
// }}}
|
||||
|
||||
// acks_expected
|
||||
// {{{
|
||||
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
|
||||
// }}}
|
||||
|
||||
// last_ack
|
||||
// {{{
|
||||
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
|
||||
|
||||
// }}}
|
||||
|
||||
// total_fifo_fill
|
||||
// {{{
|
||||
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
|
||||
// }}}
|
||||
|
||||
// total_fifo_full
|
||||
// {{{
|
||||
always @(*)
|
||||
total_fifo_full = total_fifo_fill[LGFIFO];
|
||||
// }}}
|
||||
// }}}
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Return channel
|
||||
// {{{
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
//
|
||||
|
||||
// wb_ack_fifo
|
||||
// {{{
|
||||
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);
|
||||
// }}}
|
||||
|
||||
// read_ack_fifo
|
||||
// {{{
|
||||
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
|
||||
// }}}
|
||||
|
||||
// next_acklen
|
||||
// {{{
|
||||
always @(*)
|
||||
next_acklen = fifo_ack_ln + ((acklen[0] ? 1:0)
|
||||
+ ((i_wb_ack|i_wb_err)? 0:1));
|
||||
// }}}
|
||||
|
||||
// next_acklow
|
||||
// {{{
|
||||
always @(*)
|
||||
next_acklow = fifo_ack_ln[0] + ((acklen[0] ? 1:0)
|
||||
+ ((i_wb_ack|i_wb_err)? 0:1));
|
||||
// }}}
|
||||
|
||||
// acklen, ack_last, ack_empty
|
||||
// {{{
|
||||
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
|
||||
// }}}
|
||||
|
||||
// ack_err
|
||||
// {{{
|
||||
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);
|
||||
// }}}
|
||||
|
||||
// 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;
|
||||
// }}}
|
||||
|
||||
// err_fifo_write
|
||||
// {{{
|
||||
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;
|
||||
// }}}
|
||||
|
||||
// bid_fifo - Keep track of BID's
|
||||
// {{{
|
||||
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);
|
||||
// }}}
|
||||
|
||||
// err_fifo - Keep track of error returns
|
||||
// {{{
|
||||
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
|
||||
// }}}
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Formal property section
|
||||
// {{{
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
`ifdef FORMAL
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// The following are a subset of the properties used to verify this
|
||||
// core
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
//
|
||||
// Formal only register/wire/parameter definitions
|
||||
// {{{
|
||||
localparam F_LGDEPTH = (LGFIFO>8) ? LGFIFO+1 : 10,
|
||||
F_LGRDFIFO = 72; // 9*F_LGFIFO;
|
||||
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;
|
||||
|
||||
//
|
||||
// ...
|
||||
//
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Wishbone properties
|
||||
// {{{
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
//
|
||||
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
|
||||
// }}}
|
||||
);
|
||||
|
||||
//
|
||||
// ...
|
||||
//
|
||||
|
||||
// }}}
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// AXI bus properties
|
||||
// {{{
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
//
|
||||
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)
|
||||
//
|
||||
// ...
|
||||
//
|
||||
);
|
||||
|
||||
|
||||
// never_err control(s)
|
||||
// {{{
|
||||
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
|
||||
// {{{
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
//
|
||||
|
||||
// Cover registers
|
||||
// {{{
|
||||
reg [3:0] cvr_writes, cvr_write_bursts,
|
||||
cvr_wrid_bursts;
|
||||
reg [C_AXI_ID_WIDTH-1:0] cvr_write_id;
|
||||
// }}}
|
||||
|
||||
// cvr_writes
|
||||
// {{{
|
||||
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;
|
||||
// }}}
|
||||
|
||||
// cvr_write_bursts
|
||||
// {{{
|
||||
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;
|
||||
// }}}
|
||||
|
||||
// cvr_write_id
|
||||
// {{{
|
||||
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;
|
||||
// }}}
|
||||
|
||||
// cvr_wrid_bursts
|
||||
// {{{
|
||||
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
|
||||
|
|
@ -0,0 +1,258 @@
|
|||
`default_nettype none
|
||||
`timescale 1ps / 1ps
|
||||
|
||||
module ddr3_top_axi #(
|
||||
parameter CONTROLLER_CLK_PERIOD = 12_000, //ps, clock period of the controller interface
|
||||
DDR3_CLK_PERIOD = 3_000, //ps, clock period of the DDR3 RAM device (must be 1/4 of the CONTROLLER_CLK_PERIOD)
|
||||
ROW_BITS = 14, //width of row address
|
||||
COL_BITS = 10, //width of column address
|
||||
BA_BITS = 3, //width of bank address
|
||||
BYTE_LANES = 2, //number of byte lanes of DDR3 RAM
|
||||
AXI_ID_WIDTH = 4, // The AXI id width used for R&W, an int between 1-16
|
||||
WB2_ADDR_BITS = 7, //width of 2nd wishbone address bus
|
||||
WB2_DATA_BITS = 32, //width of 2nd wishbone data bus
|
||||
/* verilator lint_off UNUSEDPARAM */
|
||||
parameter[0:0] MICRON_SIM = 0, //enable faster simulation for micron ddr3 model (shorten POWER_ON_RESET_HIGH and INITIAL_CKE_LOW)
|
||||
ODELAY_SUPPORTED = 0, //set to 1 when ODELAYE2 is supported
|
||||
SECOND_WISHBONE = 0, //set to 1 if 2nd wishbone for debugging is needed
|
||||
parameter // The next parameters act more like a localparam (since user does not have to set this manually) but was added here to simplify port declaration
|
||||
DQ_BITS = 8, //device width (fixed to 8, if DDR3 is x16 then BYTE_LANES will be 2 while )
|
||||
serdes_ratio = 4, // this controller is fixed as a 4:1 memory controller (CONTROLLER_CLK_PERIOD/DDR3_CLK_PERIOD = 4)
|
||||
wb_addr_bits = ROW_BITS + COL_BITS + BA_BITS - $clog2(serdes_ratio*2),
|
||||
wb_data_bits = DQ_BITS*BYTE_LANES*serdes_ratio*2,
|
||||
wb_sel_bits = wb_data_bits / 8,
|
||||
wb2_sel_bits = WB2_DATA_BITS / 8,
|
||||
//4 is the width of a single ddr3 command {cs_n, ras_n, cas_n, we_n} plus 3 (ck_en, odt, reset_n) plus bank bits plus row bits
|
||||
cmd_len = 4 + 3 + BA_BITS + ROW_BITS,
|
||||
AXI_LSBS = $clog2(wb_data_bits)-3,
|
||||
AXI_ADDR_WIDTH = wb_addr_bits + AXI_LSBS,
|
||||
AXI_DATA_WIDTH = wb_data_bits
|
||||
)
|
||||
(
|
||||
input wire i_controller_clk, i_ddr3_clk, i_ref_clk, //i_controller_clk = CONTROLLER_CLK_PERIOD, i_ddr3_clk = DDR3_CLK_PERIOD, i_ref_clk = 200MHz
|
||||
input wire i_ddr3_clk_90, //required only when ODELAY_SUPPORTED is zero
|
||||
input wire i_rst_n,
|
||||
//
|
||||
// AXI Interface
|
||||
// AXI write address channel signals
|
||||
input wire s_axi_awvalid,
|
||||
output wire s_axi_awready,
|
||||
input wire [AXI_ID_WIDTH-1:0] s_axi_awid,
|
||||
input wire [AXI_ADDR_WIDTH-1:0] s_axi_awaddr,
|
||||
input wire [7:0] s_axi_awlen,
|
||||
input wire [2:0] s_axi_awsize,
|
||||
input wire [1:0] s_axi_awburst,
|
||||
input wire [0:0] s_axi_awlock,
|
||||
input wire [3:0] s_axi_awcache,
|
||||
input wire [2:0] s_axi_awprot,
|
||||
input wire [3:0] s_axi_awqos,
|
||||
// AXI write data channel signals
|
||||
input wire s_axi_wvalid,
|
||||
output wire s_axi_wready,
|
||||
input wire [AXI_DATA_WIDTH-1:0] s_axi_wdata,
|
||||
input wire [AXI_DATA_WIDTH/8-1:0] s_axi_wstrb,
|
||||
input wire s_axi_wlast,
|
||||
// AXI write response channel signals
|
||||
output wire s_axi_bvalid,
|
||||
input wire s_axi_bready,
|
||||
output wire [AXI_ID_WIDTH-1:0] s_axi_bid,
|
||||
output wire [1:0] s_axi_bresp,
|
||||
// AXI read address channel signals
|
||||
input wire s_axi_arvalid,
|
||||
output wire s_axi_arready,
|
||||
input wire [AXI_ID_WIDTH-1:0] s_axi_arid,
|
||||
input wire [AXI_ADDR_WIDTH-1:0] s_axi_araddr,
|
||||
input wire [7:0] s_axi_arlen,
|
||||
input wire [2:0] s_axi_arsize,
|
||||
input wire [1:0] s_axi_arburst,
|
||||
input wire [0:0] s_axi_arlock,
|
||||
input wire [3:0] s_axi_arcache,
|
||||
input wire [2:0] s_axi_arprot,
|
||||
input wire [3:0] s_axi_arqos,
|
||||
// AXI read data channel signals
|
||||
output wire s_axi_rvalid, // rd rslt valid
|
||||
input wire s_axi_rready, // rd rslt ready
|
||||
output wire [AXI_ID_WIDTH-1:0] s_axi_rid, // response id
|
||||
output wire [AXI_DATA_WIDTH-1:0] s_axi_rdata,// read data
|
||||
output wire s_axi_rlast, // read last
|
||||
output wire [1:0] s_axi_rresp, // read response
|
||||
//
|
||||
// DDR3 I/O Interface
|
||||
output wire o_ddr3_clk_p, o_ddr3_clk_n,
|
||||
output wire o_ddr3_reset_n,
|
||||
output wire o_ddr3_cke,
|
||||
output wire o_ddr3_cs_n,
|
||||
output wire o_ddr3_ras_n,
|
||||
output wire o_ddr3_cas_n,
|
||||
output wire o_ddr3_we_n,
|
||||
output wire[ROW_BITS-1:0] o_ddr3_addr,
|
||||
output wire[BA_BITS-1:0] o_ddr3_ba_addr,
|
||||
inout wire[(DQ_BITS*BYTE_LANES)-1:0] io_ddr3_dq,
|
||||
inout wire[BYTE_LANES-1:0] io_ddr3_dqs, io_ddr3_dqs_n,
|
||||
output wire[BYTE_LANES-1:0] o_ddr3_dm,
|
||||
output wire o_ddr3_odt,
|
||||
//
|
||||
// Debug outputs
|
||||
output wire[31:0] o_debug1,
|
||||
output wire[31:0] o_debug2,
|
||||
output wire[31:0] o_debug3,
|
||||
output wire[(DQ_BITS*BYTE_LANES)/8-1:0] o_ddr3_debug_read_dqs_p,
|
||||
output wire[(DQ_BITS*BYTE_LANES)/8-1:0] o_ddr3_debug_read_dqs_n
|
||||
);
|
||||
|
||||
wire wb_cyc;
|
||||
wire wb_stb;
|
||||
wire wb_we;
|
||||
wire[wb_addr_bits-1:0] wb_addr;
|
||||
wire[wb_data_bits-1:0] o_wb_data;
|
||||
wire[wb_sel_bits-1:0] wb_sel;
|
||||
wire wb_stall;
|
||||
wire wb_ack;
|
||||
wire[wb_data_bits-1:0] i_wb_data;
|
||||
|
||||
// DDR3 Controller
|
||||
ddr3_top #(
|
||||
.CONTROLLER_CLK_PERIOD(CONTROLLER_CLK_PERIOD), //ps, clock period of the controller interface
|
||||
.DDR3_CLK_PERIOD(DDR3_CLK_PERIOD), //ps, clock period of the DDR3 RAM device (must be 1/4 of the CONTROLLER_CLK_PERIOD)
|
||||
.ROW_BITS(ROW_BITS), //width of row address
|
||||
.COL_BITS(COL_BITS), //width of column address
|
||||
.BA_BITS(BA_BITS), //width of bank address
|
||||
.BYTE_LANES(BYTE_LANES), //number of byte lanes of DDR3 RAM
|
||||
.AUX_WIDTH(AXI_ID_WIDTH), //width of aux line (must be >= 4)
|
||||
.MICRON_SIM(MICRON_SIM), //enable faster simulation for micron ddr3 model (shorten POWER_ON_RESET_HIGH and INITIAL_CKE_LOW)
|
||||
.ODELAY_SUPPORTED(ODELAY_SUPPORTED), //set to 1 if ODELAYE2 is supported
|
||||
.SECOND_WISHBONE(SECOND_WISHBONE), //set to 1 if 2nd wishbone for debugging is needed
|
||||
.WB2_ADDR_BITS(WB2_ADDR_BITS), //width of 2nd wishbone address bus
|
||||
.WB2_DATA_BITS(WB2_DATA_BITS) //width of 2nd wishbone data bus
|
||||
) ddr3_top_inst
|
||||
(
|
||||
//clock and reset
|
||||
.i_controller_clk(i_controller_clk),
|
||||
.i_ddr3_clk(i_ddr3_clk), //i_controller_clk has period of CONTROLLER_CLK_PERIOD, i_ddr3_clk has period of DDR3_CLK_PERIOD
|
||||
.i_ref_clk(i_ref_clk), // usually set to 200 MHz
|
||||
.i_ddr3_clk_90(i_ddr3_clk_90), //90 degree phase shifted version i_ddr3_clk (required only when ODELAY_SUPPORTED is zero)
|
||||
.i_rst_n(i_rst_n),
|
||||
//
|
||||
// Wishbone inputs
|
||||
.i_wb_cyc(wb_cyc), //bus cycle active (1 = normal operation, 0 = all ongoing transaction are to be cancelled)
|
||||
.i_wb_stb(wb_stb), //request a transfer
|
||||
.i_wb_we(wb_we), //write-enable (1 = write, 0 = read)
|
||||
.i_wb_addr(wb_addr), //burst-addressable {row,bank,col}
|
||||
.i_wb_data(o_wb_data), //write data, for a 4:1 controller data width is 8 times the number of pins on the device
|
||||
.i_wb_sel(wb_sel), //byte strobe for write (1 = write the byte)
|
||||
.i_aux(0), //for AXI-interface compatibility (given upon strobe)
|
||||
// Wishbone outputs
|
||||
.o_wb_stall(wb_stall), //1 = busy, cannot accept requests
|
||||
.o_wb_ack(wb_ack), //1 = read/write request has completed
|
||||
.o_wb_data(i_wb_data), //read data, for a 4:1 controller data width is 8 times the number of pins on the device
|
||||
.o_aux(),
|
||||
//
|
||||
// Wishbone 2 (PHY) inputs (WISHBONE 2 UNCONNECTED IN AXI MODE)
|
||||
.i_wb2_cyc(0), //bus cycle active (1 = normal operation, 0 = all ongoing transaction are to be cancelled)
|
||||
.i_wb2_stb(0), //request a transfer
|
||||
.i_wb2_we(0), //write-enable (1 = write, 0 = read)
|
||||
.i_wb2_addr(0), //burst-addressable {row,bank,col}
|
||||
.i_wb2_data(0), //write data, for a 4:1 controller data width is 8 times the number of pins on the device
|
||||
.i_wb2_sel(0), //byte strobe for write (1 = write the byte)
|
||||
// Wishbone 2 (Controller) outputs
|
||||
.o_wb2_stall(), //1 = busy, cannot accept requests
|
||||
.o_wb2_ack(), //1 = read/write request has completed
|
||||
.o_wb2_data(), //read data, for a 4:1 controller data width is 8 times the number of pins on the device
|
||||
//
|
||||
// DDR3 I/O Interface
|
||||
.o_ddr3_clk_p(o_ddr3_clk_p),
|
||||
.o_ddr3_clk_n(o_ddr3_clk_n),
|
||||
.o_ddr3_reset_n(o_ddr3_reset_n),
|
||||
.o_ddr3_cke(o_ddr3_cke),
|
||||
.o_ddr3_cs_n(o_ddr3_cs_n), // width = number of DDR3 ranks
|
||||
.o_ddr3_ras_n(o_ddr3_ras_n),
|
||||
.o_ddr3_cas_n(o_ddr3_cas_n),
|
||||
.o_ddr3_we_n(o_ddr3_we_n),
|
||||
.o_ddr3_addr(o_ddr3_addr), // width = ROW_BITS
|
||||
.o_ddr3_ba_addr(o_ddr3_ba_addr), // width = BA_BITS
|
||||
.io_ddr3_dq(io_ddr3_dq), // width = BYTE_LANES*8
|
||||
.io_ddr3_dqs(io_ddr3_dqs), // width = BYTE_LANES
|
||||
.io_ddr3_dqs_n(io_ddr3_dqs_n), // width = BYTE_LANES
|
||||
.o_ddr3_dm(o_ddr3_dm), // width = BYTE_LANES
|
||||
.o_ddr3_odt(o_ddr3_odt),
|
||||
//
|
||||
// Debug outputs
|
||||
.o_debug1(o_debug1),
|
||||
.o_debug2(o_debug2),
|
||||
.o_debug3(o_debug3),
|
||||
.o_ddr3_debug_read_dqs_p(o_ddr3_debug_read_dqs_p),
|
||||
.o_ddr3_debug_read_dqs_n(o_ddr3_debug_read_dqs_n)
|
||||
////////////////////////////////////
|
||||
);
|
||||
|
||||
axim2wbsp #(
|
||||
.C_AXI_ID_WIDTH(AXI_ID_WIDTH), // The AXI id width used for R&W, an int between 1-16
|
||||
.C_AXI_DATA_WIDTH(AXI_DATA_WIDTH),// Width of the AXI R&W data
|
||||
.C_AXI_ADDR_WIDTH(AXI_ADDR_WIDTH), // AXI Address width
|
||||
.LGFIFO(5),
|
||||
.OPT_SWAP_ENDIANNESS(0),
|
||||
.OPT_READONLY(0),
|
||||
.OPT_WRITEONLY(0)
|
||||
) axim2wbsp_inst (
|
||||
.S_AXI_ACLK(i_controller_clk), // System clock
|
||||
.S_AXI_ARESETN(i_rst_n),
|
||||
// AXI write address channel signals
|
||||
.S_AXI_AWVALID(s_axi_awvalid),
|
||||
.S_AXI_AWREADY(s_axi_awready),
|
||||
.S_AXI_AWID(s_axi_awid),
|
||||
.S_AXI_AWADDR(s_axi_awaddr),
|
||||
.S_AXI_AWLEN(s_axi_awlen),
|
||||
.S_AXI_AWSIZE(s_axi_awsize),
|
||||
.S_AXI_AWBURST(s_axi_awburst),
|
||||
.S_AXI_AWLOCK(s_axi_awlock),
|
||||
.S_AXI_AWCACHE(s_axi_awcache),
|
||||
.S_AXI_AWPROT(s_axi_awprot),
|
||||
.S_AXI_AWQOS(s_axi_awqos),
|
||||
// AXI write data channel signals
|
||||
.S_AXI_WVALID(s_axi_wvalid),
|
||||
.S_AXI_WREADY(s_axi_wready),
|
||||
.S_AXI_WDATA(s_axi_wdata),
|
||||
.S_AXI_WSTRB(s_axi_wstrb),
|
||||
.S_AXI_WLAST(s_axi_wlast),
|
||||
// AXI write response channel signals
|
||||
.S_AXI_BVALID(s_axi_bvalid),
|
||||
.S_AXI_BREADY(s_axi_bready),
|
||||
.S_AXI_BID(s_axi_bid),
|
||||
.S_AXI_BRESP(s_axi_bresp),
|
||||
// AXI read address channel signals
|
||||
.S_AXI_ARVALID(s_axi_arvalid),
|
||||
.S_AXI_ARREADY(s_axi_arready),
|
||||
.S_AXI_ARID(s_axi_arid),
|
||||
.S_AXI_ARADDR(s_axi_araddr),
|
||||
.S_AXI_ARLEN(s_axi_arlen),
|
||||
.S_AXI_ARSIZE(s_axi_arsize),
|
||||
.S_AXI_ARBURST(s_axi_arburst),
|
||||
.S_AXI_ARLOCK(s_axi_arlock),
|
||||
.S_AXI_ARCACHE(s_axi_arcache),
|
||||
.S_AXI_ARPROT(s_axi_arprot),
|
||||
.S_AXI_ARQOS(s_axi_arqos),
|
||||
// AXI read data channel signals
|
||||
.S_AXI_RVALID(s_axi_rvalid), // Rd rslt valid
|
||||
.S_AXI_RREADY(s_axi_rready), // Rd rslt ready
|
||||
.S_AXI_RID(s_axi_rid), // Response ID
|
||||
.S_AXI_RDATA(s_axi_rdata),// Read data
|
||||
.S_AXI_RLAST(s_axi_rlast), // Read last
|
||||
.S_AXI_RRESP(s_axi_rresp), // Read response
|
||||
// We'll share the clock and the reset
|
||||
.o_reset(),
|
||||
.o_wb_cyc(wb_cyc),
|
||||
.o_wb_stb(wb_stb),
|
||||
.o_wb_we(wb_we),
|
||||
.o_wb_addr(wb_addr),
|
||||
.o_wb_data(o_wb_data),
|
||||
.o_wb_sel(wb_sel),
|
||||
.i_wb_stall(wb_stall),
|
||||
.i_wb_ack(wb_ack),
|
||||
.i_wb_data(i_wb_data),
|
||||
.i_wb_err(0)
|
||||
);
|
||||
|
||||
|
||||
|
||||
endmodule
|
||||
|
||||
|
|
@ -0,0 +1,313 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Filename: migsdram.v
|
||||
// {{{
|
||||
// Project: WB2AXIPSP: bus bridges and other odds and ends
|
||||
//
|
||||
// Purpose: This file isn't really a part of the synthesis implementation
|
||||
// of the wb2axip project itself, but rather it is an example
|
||||
// of how the wb2axip project can be used to connect a MIG generated
|
||||
// IP component.
|
||||
//
|
||||
// This implementation depends upon the existence of a MIG generated
|
||||
// core, named "mig_axis", and illustrates how such a core might be
|
||||
// connected to the wbm2axip bridge. Specific options of the mig_axis
|
||||
// setup include 6 identifier bits, and a full-sized bus width of 128
|
||||
// bits. These two settings are both appropriate for driving a DDR3
|
||||
// memory (whose minimum transfer size is 128 bits), but may need to be
|
||||
// adjusted to support other memories.
|
||||
//
|
||||
// Creator: Dan Gisselquist, Ph.D.
|
||||
// Gisselquist Technology, LLC
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// }}}
|
||||
// Copyright (C) 2015-2024, 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 migsdram(i_clk, i_clk_200mhz, o_sys_clk, i_rst, o_sys_reset,
|
||||
// Wishbone components
|
||||
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,
|
||||
// SDRAM connections
|
||||
o_ddr_ck_p, o_ddr_ck_n,
|
||||
o_ddr_reset_n, o_ddr_cke,
|
||||
o_ddr_cs_n, o_ddr_ras_n, o_ddr_cas_n, o_ddr_we_n,
|
||||
o_ddr_ba, o_ddr_addr,
|
||||
o_ddr_odt, o_ddr_dm,
|
||||
io_ddr_dqs_p, io_ddr_dqs_n,
|
||||
io_ddr_data
|
||||
);
|
||||
parameter DDRWIDTH = 16, WBDATAWIDTH=32;
|
||||
parameter AXIDWIDTH = 6;
|
||||
// The SDRAM address bits (RAMABITS) are a touch more difficult to work
|
||||
// out. Here we leave them as a fixed parameter, but there are
|
||||
// consequences to this. Specifically, the wishbone data width, the
|
||||
// wishbone address width, and this number have interactions not
|
||||
// well captured here.
|
||||
parameter RAMABITS = 28;
|
||||
// All DDR3 memories have 8 timeslots. This, if the DDR3 memory
|
||||
// has 16 bits to it (as above), the entire transaction must take
|
||||
// AXIWIDTH bits ...
|
||||
localparam AXIWIDTH= DDRWIDTH *8;
|
||||
localparam DW=WBDATAWIDTH;
|
||||
localparam AW=(WBDATAWIDTH==32)? RAMABITS-2
|
||||
:((WBDATAWIDTH==64) ? RAMABITS-3
|
||||
:((WBDATAWIDTH==128) ? RAMABITS-4
|
||||
: RAMABITS-5)); // (WBDATAWIDTH==256)
|
||||
localparam SELW= (WBDATAWIDTH/8);
|
||||
//
|
||||
input wire i_clk, i_clk_200mhz, i_rst;
|
||||
output wire o_sys_clk;
|
||||
output reg o_sys_reset;
|
||||
//
|
||||
input wire i_wb_cyc, i_wb_stb, i_wb_we;
|
||||
input wire [(AW-1):0] i_wb_addr;
|
||||
input wire [(DW-1):0] i_wb_data;
|
||||
input wire [(SELW-1):0] i_wb_sel;
|
||||
output wire o_wb_ack, o_wb_stall;
|
||||
output wire [(DW-1):0] o_wb_data;
|
||||
output wire o_wb_err;
|
||||
//
|
||||
output wire [0:0] o_ddr_ck_p, o_ddr_ck_n;
|
||||
output wire [0:0] o_ddr_cke;
|
||||
output wire o_ddr_reset_n,
|
||||
o_ddr_ras_n, o_ddr_cas_n, o_ddr_we_n;
|
||||
output wire [0:0] o_ddr_cs_n;
|
||||
output wire [2:0] o_ddr_ba;
|
||||
output wire [13:0] o_ddr_addr;
|
||||
output wire [0:0] o_ddr_odt;
|
||||
output wire [(DDRWIDTH/8-1):0] o_ddr_dm;
|
||||
inout wire [1:0] io_ddr_dqs_p, io_ddr_dqs_n;
|
||||
inout wire [(DDRWIDTH-1):0] io_ddr_data;
|
||||
|
||||
|
||||
`define SDRAM_ACCESS
|
||||
`ifdef SDRAM_ACCESS
|
||||
|
||||
wire aresetn;
|
||||
assign aresetn = 1'b1; // Never reset
|
||||
|
||||
// Write address channel
|
||||
wire [(AXIDWIDTH-1):0] s_axi_awid;
|
||||
wire [(RAMABITS-1):0] s_axi_awaddr;
|
||||
wire [7:0] s_axi_awlen;
|
||||
wire [2:0] s_axi_awsize;
|
||||
wire [1:0] s_axi_awburst;
|
||||
wire [0:0] s_axi_awlock;
|
||||
wire [3:0] s_axi_awcache;
|
||||
wire [2:0] s_axi_awprot;
|
||||
wire [3:0] s_axi_awqos;
|
||||
wire s_axi_awvalid;
|
||||
wire s_axi_awready;
|
||||
// Writei data channel
|
||||
wire [(AXIWIDTH-1):0] s_axi_wdata;
|
||||
wire [(AXIWIDTH/8-1):0] s_axi_wstrb;
|
||||
wire s_axi_wlast, s_axi_wvalid, s_axi_wready;
|
||||
// Write response channel
|
||||
wire s_axi_bready;
|
||||
wire [(AXIDWIDTH-1):0] s_axi_bid;
|
||||
wire [1:0] s_axi_bresp;
|
||||
wire s_axi_bvalid;
|
||||
|
||||
// Read address channel
|
||||
wire [(AXIDWIDTH-1):0] s_axi_arid;
|
||||
wire [(RAMABITS-1):0] s_axi_araddr;
|
||||
wire [7:0] s_axi_arlen;
|
||||
wire [2:0] s_axi_arsize;
|
||||
wire [1:0] s_axi_arburst;
|
||||
wire [0:0] s_axi_arlock;
|
||||
wire [3:0] s_axi_arcache;
|
||||
wire [2:0] s_axi_arprot;
|
||||
wire [3:0] s_axi_arqos;
|
||||
wire s_axi_arvalid;
|
||||
wire s_axi_arready;
|
||||
// Read response/data channel
|
||||
wire [(AXIDWIDTH-1):0] s_axi_rid;
|
||||
wire [(AXIWIDTH-1):0] s_axi_rdata;
|
||||
wire [1:0] s_axi_rresp;
|
||||
wire s_axi_rlast;
|
||||
wire s_axi_rvalid;
|
||||
wire s_axi_rready;
|
||||
|
||||
// Other wires ...
|
||||
wire init_calib_complete, mmcm_locked;
|
||||
wire app_sr_active, app_ref_ack, app_zq_ack;
|
||||
wire app_sr_req, app_ref_req, app_zq_req;
|
||||
wire w_sys_reset;
|
||||
wire [11:0] w_device_temp;
|
||||
|
||||
|
||||
mig_axis mig_sdram(
|
||||
.ddr3_ck_p(o_ddr_ck_p), .ddr3_ck_n(o_ddr_ck_n),
|
||||
.ddr3_reset_n(o_ddr_reset_n), .ddr3_cke(o_ddr_cke),
|
||||
.ddr3_cs_n(o_ddr_cs_n), .ddr3_ras_n(o_ddr_ras_n),
|
||||
.ddr3_we_n(o_ddr_we_n), .ddr3_cas_n(o_ddr_cas_n),
|
||||
.ddr3_ba(o_ddr_ba), .ddr3_addr(o_ddr_addr),
|
||||
.ddr3_odt(o_ddr_odt),
|
||||
.ddr3_dqs_p(io_ddr_dqs_p), .ddr3_dqs_n(io_ddr_dqs_n),
|
||||
.ddr3_dq(io_ddr_data), .ddr3_dm(o_ddr_dm),
|
||||
//
|
||||
.sys_clk_i(i_clk),
|
||||
.clk_ref_i(i_clk_200mhz),
|
||||
.ui_clk(o_sys_clk),
|
||||
.ui_clk_sync_rst(w_sys_reset),
|
||||
.mmcm_locked(mmcm_locked),
|
||||
.aresetn(aresetn),
|
||||
.app_sr_req(1'b0),
|
||||
.app_ref_req(1'b0),
|
||||
.app_zq_req(1'b0),
|
||||
.app_sr_active(app_sr_active),
|
||||
.app_ref_ack(app_ref_ack),
|
||||
.app_zq_ack(app_zq_ack),
|
||||
//
|
||||
.s_axi_awid(s_axi_awid), .s_axi_awaddr(s_axi_awaddr),
|
||||
.s_axi_awlen(s_axi_awlen), .s_axi_awsize(s_axi_awsize),
|
||||
.s_axi_awburst(s_axi_awburst), .s_axi_awlock(s_axi_awlock),
|
||||
.s_axi_awcache(s_axi_awcache), .s_axi_awprot(s_axi_awprot),
|
||||
.s_axi_awqos(s_axi_awqos), .s_axi_awvalid(s_axi_awvalid),
|
||||
.s_axi_awready(s_axi_awready),
|
||||
//
|
||||
.s_axi_wready( s_axi_wready),
|
||||
.s_axi_wdata( s_axi_wdata),
|
||||
.s_axi_wstrb( s_axi_wstrb),
|
||||
.s_axi_wlast( s_axi_wlast),
|
||||
.s_axi_wvalid( s_axi_wvalid),
|
||||
//
|
||||
.s_axi_bready(s_axi_bready), .s_axi_bid(s_axi_bid),
|
||||
.s_axi_bresp(s_axi_bresp), .s_axi_bvalid(s_axi_bvalid),
|
||||
//
|
||||
.s_axi_arid(s_axi_arid), .s_axi_araddr(s_axi_araddr),
|
||||
.s_axi_arlen(s_axi_arlen), .s_axi_arsize(s_axi_arsize),
|
||||
.s_axi_arburst(s_axi_arburst), .s_axi_arlock(s_axi_arlock),
|
||||
.s_axi_arcache(s_axi_arcache), .s_axi_arprot(s_axi_arprot),
|
||||
.s_axi_arqos(s_axi_arqos), .s_axi_arvalid(s_axi_arvalid),
|
||||
.s_axi_arready(s_axi_arready),
|
||||
//
|
||||
.s_axi_rready(s_axi_rready), .s_axi_rid(s_axi_rid),
|
||||
.s_axi_rdata(s_axi_rdata), .s_axi_rresp(s_axi_rresp),
|
||||
.s_axi_rlast(s_axi_rlast), .s_axi_rvalid(s_axi_rvalid),
|
||||
.init_calib_complete(init_calib_complete),
|
||||
.sys_rst(i_rst),
|
||||
.device_temp(w_device_temp)
|
||||
);
|
||||
|
||||
wbm2axisp #(
|
||||
.C_AXI_ID_WIDTH(AXIDWIDTH),
|
||||
.C_AXI_DATA_WIDTH(AXIWIDTH),
|
||||
.C_AXI_ADDR_WIDTH(RAMABITS),
|
||||
.AW(AW), .DW(DW)
|
||||
)
|
||||
bus_translator(
|
||||
.i_clk(o_sys_clk),
|
||||
// .i_reset(i_rst), // internally unused
|
||||
// Write address channel signals
|
||||
.o_axi_awvalid( s_axi_awvalid),
|
||||
.i_axi_awready( s_axi_awready),
|
||||
.o_axi_awid( s_axi_awid),
|
||||
.o_axi_awaddr( s_axi_awaddr),
|
||||
.o_axi_awlen( s_axi_awlen),
|
||||
.o_axi_awsize( s_axi_awsize),
|
||||
.o_axi_awburst( s_axi_awburst),
|
||||
.o_axi_awlock( s_axi_awlock),
|
||||
.o_axi_awcache( s_axi_awcache),
|
||||
.o_axi_awprot( s_axi_awprot), // s_axi_awqos
|
||||
.o_axi_awqos( s_axi_awqos), // s_axi_awqos
|
||||
//
|
||||
.o_axi_wvalid( s_axi_wvalid),
|
||||
.i_axi_wready( s_axi_wready),
|
||||
.o_axi_wdata( s_axi_wdata),
|
||||
.o_axi_wstrb( s_axi_wstrb),
|
||||
.o_axi_wlast( s_axi_wlast),
|
||||
//
|
||||
.i_axi_bvalid( s_axi_bvalid),
|
||||
.o_axi_bready( s_axi_bready),
|
||||
.i_axi_bid( s_axi_bid),
|
||||
.i_axi_bresp( s_axi_bresp),
|
||||
//
|
||||
.o_axi_arvalid( s_axi_arvalid),
|
||||
.i_axi_arready( s_axi_arready),
|
||||
.o_axi_arid( s_axi_arid),
|
||||
.o_axi_araddr( s_axi_araddr),
|
||||
.o_axi_arlen( s_axi_arlen),
|
||||
.o_axi_arsize( s_axi_arsize),
|
||||
.o_axi_arburst( s_axi_arburst),
|
||||
.o_axi_arlock( s_axi_arlock),
|
||||
.o_axi_arcache( s_axi_arcache),
|
||||
.o_axi_arprot( s_axi_arprot),
|
||||
.o_axi_arqos( s_axi_arqos),
|
||||
//
|
||||
.i_axi_rvalid( s_axi_rvalid),
|
||||
.o_axi_rready( s_axi_rready),
|
||||
.i_axi_rid( s_axi_rid),
|
||||
.i_axi_rdata( s_axi_rdata),
|
||||
.i_axi_rresp( s_axi_rresp),
|
||||
.i_axi_rlast( s_axi_rlast),
|
||||
//
|
||||
.i_wb_cyc( i_wb_cyc),
|
||||
.i_wb_stb( i_wb_stb),
|
||||
.i_wb_we( i_wb_we),
|
||||
.i_wb_addr( i_wb_addr),
|
||||
.i_wb_data( i_wb_data),
|
||||
.i_wb_sel( i_wb_sel),
|
||||
//
|
||||
.o_wb_stall( o_wb_stall),
|
||||
.o_wb_ack( o_wb_ack),
|
||||
.o_wb_data( o_wb_data),
|
||||
.o_wb_err( o_wb_err)
|
||||
);
|
||||
|
||||
// Convert from active low to active high, *and* hold the system in
|
||||
// reset until the memory comes up.
|
||||
initial o_sys_reset = 1'b1;
|
||||
always @(posedge o_sys_clk)
|
||||
o_sys_reset <= (!w_sys_reset)
|
||||
||(!init_calib_complete)
|
||||
||(!mmcm_locked);
|
||||
`else
|
||||
BUFG sysclk(i_clk, o_sys_clk);
|
||||
initial o_sys_reset <= 1'b1;
|
||||
always @(posedge i_clk)
|
||||
o_sys_reset <= 1'b1;
|
||||
|
||||
OBUFDS ckobuf(.I(i_clk), .O(o_ddr_ck_p), .OB(o_ddr_ck_n));
|
||||
|
||||
assign o_ddr_reset_n = 1'b0;
|
||||
assign o_ddr_cke[0] = 1'b0;
|
||||
assign o_ddr_cs_n[0] = 1'b1;
|
||||
assign o_ddr_cas_n = 1'b1;
|
||||
assign o_ddr_ras_n = 1'b1;
|
||||
assign o_ddr_we_n = 1'b1;
|
||||
assign o_ddr_ba = 3'h0;
|
||||
assign o_ddr_addr = 14'h00;
|
||||
assign o_ddr_dm = 2'b00;
|
||||
assign io_ddr_data = 16'h0;
|
||||
|
||||
OBUFDS dqsbufa(.I(i_clk), .O(io_ddr_dqs_p[1]), .OB(io_ddr_dqs_n[1]));
|
||||
OBUFDS dqsbufb(.I(i_clk), .O(io_ddr_dqs_p[0]), .OB(io_ddr_dqs_n[0]));
|
||||
|
||||
`endif
|
||||
|
||||
endmodule
|
||||
`ifndef YOSYS
|
||||
`default_nettype wire
|
||||
`endif
|
||||
|
|
@ -0,0 +1,482 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Filename: sfifo.v
|
||||
// {{{
|
||||
// Project: WB2AXIPSP: bus bridges and other odds and ends
|
||||
//
|
||||
// Purpose: A synchronous data FIFO.
|
||||
//
|
||||
// Creator: Dan Gisselquist, Ph.D.
|
||||
// Gisselquist Technology, LLC
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Written and distributed by Gisselquist Technology, LLC
|
||||
// }}}
|
||||
// This design is hereby granted to the public domain.
|
||||
// {{{
|
||||
// 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.
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
`default_nettype none
|
||||
// }}}
|
||||
module sfifo #(
|
||||
// {{{
|
||||
parameter BW=8, // Byte/data width
|
||||
parameter LGFLEN=4,
|
||||
parameter [0:0] OPT_ASYNC_READ = 1'b1,
|
||||
parameter [0:0] OPT_WRITE_ON_FULL = 1'b0,
|
||||
parameter [0:0] OPT_READ_ON_EMPTY = 1'b0
|
||||
// }}}
|
||||
) (
|
||||
// {{{
|
||||
input wire i_clk,
|
||||
input wire i_reset,
|
||||
//
|
||||
// Write interface
|
||||
input wire i_wr,
|
||||
input wire [(BW-1):0] i_data,
|
||||
output wire o_full,
|
||||
output reg [LGFLEN:0] o_fill,
|
||||
//
|
||||
// Read interface
|
||||
input wire i_rd,
|
||||
output reg [(BW-1):0] o_data,
|
||||
output wire o_empty // True if FIFO is empty
|
||||
`ifdef FORMAL
|
||||
`ifdef F_PEEK
|
||||
, output wire [LGFLEN:0] f_first_addr,
|
||||
output wire [LGFLEN:0] f_second_addr,
|
||||
output reg [BW-1:0] f_first_data, f_second_data,
|
||||
|
||||
output reg f_first_in_fifo,
|
||||
f_second_in_fifo,
|
||||
output reg [LGFLEN:0] f_distance_to_first,
|
||||
f_distance_to_second
|
||||
`endif
|
||||
`endif
|
||||
// }}}
|
||||
);
|
||||
|
||||
// Register/net declarations
|
||||
// {{{
|
||||
localparam FLEN=(1<<LGFLEN);
|
||||
reg r_full, r_empty;
|
||||
reg [(BW-1):0] mem[0:(FLEN-1)];
|
||||
reg [LGFLEN:0] wr_addr, rd_addr;
|
||||
|
||||
wire w_wr = (i_wr && !o_full);
|
||||
wire w_rd = (i_rd && !o_empty);
|
||||
// }}}
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Write half
|
||||
// {{{
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
//
|
||||
|
||||
// o_fill
|
||||
// {{{
|
||||
initial o_fill = 0;
|
||||
always @(posedge i_clk)
|
||||
if (i_reset)
|
||||
o_fill <= 0;
|
||||
else case({ w_wr, w_rd })
|
||||
2'b01: o_fill <= o_fill - 1;
|
||||
2'b10: o_fill <= o_fill + 1;
|
||||
default: o_fill <= wr_addr - rd_addr;
|
||||
endcase
|
||||
// }}}
|
||||
|
||||
// r_full, o_full
|
||||
// {{{
|
||||
initial r_full = 0;
|
||||
always @(posedge i_clk)
|
||||
if (i_reset)
|
||||
r_full <= 0;
|
||||
else case({ w_wr, w_rd})
|
||||
2'b01: r_full <= 1'b0;
|
||||
2'b10: r_full <= (o_fill == { 1'b0, {(LGFLEN){1'b1}} });
|
||||
default: r_full <= (o_fill == { 1'b1, {(LGFLEN){1'b0}} });
|
||||
endcase
|
||||
|
||||
assign o_full = (i_rd && OPT_WRITE_ON_FULL) ? 1'b0 : r_full;
|
||||
// }}}
|
||||
|
||||
// wr_addr, the write address pointer
|
||||
// {{{
|
||||
initial wr_addr = 0;
|
||||
always @(posedge i_clk)
|
||||
if (i_reset)
|
||||
wr_addr <= 0;
|
||||
else if (w_wr)
|
||||
wr_addr <= wr_addr + 1'b1;
|
||||
// }}}
|
||||
|
||||
// Write to memory
|
||||
// {{{
|
||||
always @(posedge i_clk)
|
||||
if (w_wr)
|
||||
mem[wr_addr[(LGFLEN-1):0]] <= i_data;
|
||||
// }}}
|
||||
// }}}
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Read half
|
||||
// {{{
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
//
|
||||
|
||||
// rd_addr, the read address pointer
|
||||
// {{{
|
||||
initial rd_addr = 0;
|
||||
always @(posedge i_clk)
|
||||
if (i_reset)
|
||||
rd_addr <= 0;
|
||||
else if (w_rd)
|
||||
rd_addr <= rd_addr + 1;
|
||||
// }}}
|
||||
|
||||
// r_empty, o_empty
|
||||
// {{{
|
||||
initial r_empty = 1'b1;
|
||||
always @(posedge i_clk)
|
||||
if (i_reset)
|
||||
r_empty <= 1'b1;
|
||||
else case ({ w_wr, w_rd })
|
||||
2'b01: r_empty <= (o_fill <= 1);
|
||||
2'b10: r_empty <= 1'b0;
|
||||
default: begin end
|
||||
endcase
|
||||
|
||||
assign o_empty = (OPT_READ_ON_EMPTY && i_wr) ? 1'b0 : r_empty;
|
||||
// }}}
|
||||
|
||||
// Read from the FIFO
|
||||
// {{{
|
||||
generate if (OPT_ASYNC_READ && OPT_READ_ON_EMPTY)
|
||||
begin : ASYNCHRONOUS_READ_ON_EMPTY
|
||||
// o_data
|
||||
// {{{
|
||||
always @(*)
|
||||
begin
|
||||
o_data = mem[rd_addr[LGFLEN-1:0]];
|
||||
if (r_empty)
|
||||
o_data = i_data;
|
||||
end
|
||||
// }}}
|
||||
end else if (OPT_ASYNC_READ)
|
||||
begin : ASYNCHRONOUS_READ
|
||||
// o_data
|
||||
// {{{
|
||||
always @(*)
|
||||
o_data = mem[rd_addr[LGFLEN-1:0]];
|
||||
// }}}
|
||||
end else begin : REGISTERED_READ
|
||||
// {{{
|
||||
reg bypass_valid;
|
||||
reg [BW-1:0] bypass_data, rd_data;
|
||||
reg [LGFLEN-1:0] rd_next;
|
||||
|
||||
always @(*)
|
||||
rd_next = rd_addr[LGFLEN-1:0] + 1;
|
||||
|
||||
// Memory read, bypassing it if we must
|
||||
// {{{
|
||||
initial bypass_valid = 0;
|
||||
always @(posedge i_clk)
|
||||
if (i_reset)
|
||||
bypass_valid <= 0;
|
||||
else if (r_empty || i_rd)
|
||||
begin
|
||||
if (!i_wr)
|
||||
bypass_valid <= 1'b0;
|
||||
else if (r_empty || (i_rd && (o_fill == 1)))
|
||||
bypass_valid <= 1'b1;
|
||||
else
|
||||
bypass_valid <= 1'b0;
|
||||
end
|
||||
|
||||
always @(posedge i_clk)
|
||||
if (r_empty || i_rd)
|
||||
bypass_data <= i_data;
|
||||
|
||||
initial mem[0] = 0;
|
||||
initial rd_data = 0;
|
||||
always @(posedge i_clk)
|
||||
if (w_rd)
|
||||
rd_data <= mem[rd_next];
|
||||
|
||||
always @(*)
|
||||
if (OPT_READ_ON_EMPTY && r_empty)
|
||||
o_data = i_data;
|
||||
else if (bypass_valid)
|
||||
o_data = bypass_data;
|
||||
else
|
||||
o_data = rd_data;
|
||||
// }}}
|
||||
// }}}
|
||||
end endgenerate
|
||||
// }}}
|
||||
// }}}
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// FORMAL METHODS
|
||||
// {{{
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
`ifdef FORMAL
|
||||
|
||||
//
|
||||
// Assumptions about our input(s)
|
||||
//
|
||||
//
|
||||
`ifdef SFIFO
|
||||
`define ASSUME assume
|
||||
`else
|
||||
`define ASSUME assert
|
||||
`endif
|
||||
|
||||
reg f_past_valid;
|
||||
wire [LGFLEN:0] f_fill, f_next;
|
||||
wire f_empty;
|
||||
|
||||
initial f_past_valid = 1'b0;
|
||||
always @(posedge i_clk)
|
||||
f_past_valid <= 1'b1;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Assertions about our flags and counters
|
||||
// {{{
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
//
|
||||
|
||||
assign f_fill = wr_addr - rd_addr;
|
||||
assign f_empty = (wr_addr == rd_addr);
|
||||
assign f_next = rd_addr + 1'b1;
|
||||
|
||||
always @(*)
|
||||
begin
|
||||
assert(f_fill <= { 1'b1, {(LGFLEN){1'b0}} });
|
||||
assert(o_fill == f_fill);
|
||||
|
||||
assert(r_full == (f_fill == {1'b1, {(LGFLEN){1'b0}} }));
|
||||
assert(r_empty == (f_fill == 0));
|
||||
|
||||
if (!OPT_WRITE_ON_FULL)
|
||||
begin
|
||||
assert(o_full == r_full);
|
||||
end else begin
|
||||
assert(o_full == (r_full && !i_rd));
|
||||
end
|
||||
|
||||
if (!OPT_READ_ON_EMPTY)
|
||||
begin
|
||||
assert(o_empty == r_empty);
|
||||
end else begin
|
||||
assert(o_empty == (r_empty && !i_wr));
|
||||
end
|
||||
end
|
||||
|
||||
always @(posedge i_clk)
|
||||
if (!OPT_ASYNC_READ && f_past_valid)
|
||||
begin
|
||||
if (f_fill == 0)
|
||||
begin
|
||||
assert(r_empty);
|
||||
assert(o_empty || (OPT_READ_ON_EMPTY && i_wr));
|
||||
end else if ($past(f_fill)>1)
|
||||
begin
|
||||
assert(!r_empty);
|
||||
end else if ($past(!i_rd && f_fill > 0))
|
||||
assert(!r_empty);
|
||||
end
|
||||
|
||||
always @(*)
|
||||
if (!r_empty)
|
||||
begin
|
||||
// This also applies for the registered read case
|
||||
assert(mem[rd_addr[LGFLEN-1:0]] == o_data);
|
||||
end else if (OPT_READ_ON_EMPTY)
|
||||
assert(o_data == i_data);
|
||||
|
||||
// }}}
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Formal contract: (Twin write test)
|
||||
// {{{
|
||||
// If you write two values in succession, you should be able to read
|
||||
// those same two values in succession some time later.
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
//
|
||||
|
||||
// Verilator lint_off UNDRIVEN
|
||||
(* anyconst *) reg [LGFLEN:0] fw_first_addr;
|
||||
// Verilator lint_on UNDRIVEN
|
||||
`ifndef F_PEEK
|
||||
wire [LGFLEN:0] f_first_addr;
|
||||
wire [LGFLEN:0] f_second_addr;
|
||||
reg [BW-1:0] f_first_data, f_second_data;
|
||||
|
||||
reg f_first_in_fifo, f_second_in_fifo;
|
||||
reg [LGFLEN:0] f_distance_to_first, f_distance_to_second;
|
||||
`endif
|
||||
reg f_first_addr_in_fifo, f_second_addr_in_fifo;
|
||||
|
||||
assign f_first_addr = fw_first_addr;
|
||||
assign f_second_addr = f_first_addr + 1;
|
||||
|
||||
always @(*)
|
||||
begin
|
||||
f_distance_to_first = (f_first_addr - rd_addr);
|
||||
f_first_addr_in_fifo = 0;
|
||||
if ((f_fill != 0) && (f_distance_to_first < f_fill))
|
||||
f_first_addr_in_fifo = 1;
|
||||
end
|
||||
|
||||
always @(*)
|
||||
begin
|
||||
f_distance_to_second = (f_second_addr - rd_addr);
|
||||
f_second_addr_in_fifo = 0;
|
||||
if ((f_fill != 0) && (f_distance_to_second < f_fill))
|
||||
f_second_addr_in_fifo = 1;
|
||||
end
|
||||
|
||||
always @(posedge i_clk)
|
||||
if (w_wr && wr_addr == f_first_addr)
|
||||
f_first_data <= i_data;
|
||||
|
||||
always @(posedge i_clk)
|
||||
if (w_wr && wr_addr == f_second_addr)
|
||||
f_second_data <= i_data;
|
||||
|
||||
always @(*)
|
||||
if (f_first_addr_in_fifo)
|
||||
assert(mem[f_first_addr[LGFLEN-1:0]] == f_first_data);
|
||||
always @(*)
|
||||
f_first_in_fifo = (f_first_addr_in_fifo && (mem[f_first_addr[LGFLEN-1:0]] == f_first_data));
|
||||
|
||||
always @(*)
|
||||
if (f_second_addr_in_fifo)
|
||||
assert(mem[f_second_addr[LGFLEN-1:0]] == f_second_data);
|
||||
|
||||
always @(*)
|
||||
f_second_in_fifo = (f_second_addr_in_fifo && (mem[f_second_addr[LGFLEN-1:0]] == f_second_data));
|
||||
|
||||
always @(*)
|
||||
if (f_first_in_fifo && (o_fill == 1 || f_distance_to_first == 0))
|
||||
assert(o_data == f_first_data);
|
||||
|
||||
always @(*)
|
||||
if (f_second_in_fifo && (o_fill == 1 || f_distance_to_second == 0))
|
||||
assert(o_data == f_second_data);
|
||||
|
||||
always @(posedge i_clk)
|
||||
if (f_past_valid && !$past(i_reset))
|
||||
begin
|
||||
case({$past(f_first_in_fifo), $past(f_second_in_fifo)})
|
||||
2'b00: begin
|
||||
if ($past(w_wr && (!w_rd || !r_empty))
|
||||
&&($past(wr_addr == f_first_addr)))
|
||||
begin
|
||||
assert(f_first_in_fifo);
|
||||
end else begin
|
||||
assert(!f_first_in_fifo);
|
||||
end
|
||||
//
|
||||
// The second could be in the FIFO, since
|
||||
// one might write other data than f_first_data
|
||||
//
|
||||
// assert(!f_second_in_fifo);
|
||||
end
|
||||
2'b01: begin
|
||||
assert(!f_first_in_fifo);
|
||||
if ($past(w_rd && (rd_addr==f_second_addr)))
|
||||
begin
|
||||
assert((o_empty&&!OPT_ASYNC_READ)||!f_second_in_fifo);
|
||||
end else begin
|
||||
assert(f_second_in_fifo);
|
||||
end
|
||||
end
|
||||
2'b10: begin
|
||||
if ($past(w_wr)
|
||||
&&($past(wr_addr == f_second_addr)))
|
||||
begin
|
||||
assert(f_second_in_fifo);
|
||||
end else begin
|
||||
assert(!f_second_in_fifo);
|
||||
end
|
||||
if ($past(!w_rd ||(rd_addr != f_first_addr)))
|
||||
assert(f_first_in_fifo);
|
||||
end
|
||||
2'b11: begin
|
||||
assert(f_second_in_fifo);
|
||||
if ($past(!w_rd ||(rd_addr != f_first_addr)))
|
||||
begin
|
||||
assert(f_first_in_fifo);
|
||||
if (rd_addr == f_first_addr)
|
||||
assert(o_data == f_first_data);
|
||||
end else begin
|
||||
assert(!f_first_in_fifo);
|
||||
assert(o_data == f_second_data);
|
||||
end
|
||||
end
|
||||
endcase
|
||||
end
|
||||
// }}}
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Cover properties
|
||||
// {{{
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
//
|
||||
`ifdef SFIFO
|
||||
reg f_was_full;
|
||||
initial f_was_full = 0;
|
||||
always @(posedge i_clk)
|
||||
if (o_full)
|
||||
f_was_full <= 1;
|
||||
|
||||
always @(posedge i_clk)
|
||||
cover($fell(f_empty));
|
||||
|
||||
always @(posedge i_clk)
|
||||
cover($fell(o_empty));
|
||||
|
||||
always @(posedge i_clk)
|
||||
cover(f_was_full && f_empty);
|
||||
|
||||
always @(posedge i_clk)
|
||||
cover($past(o_full,2)&&(!$past(o_full))&&(o_full));
|
||||
|
||||
always @(posedge i_clk)
|
||||
if (f_past_valid)
|
||||
cover($past(o_empty,2)&&(!$past(o_empty))&& o_empty);
|
||||
`endif
|
||||
// }}}
|
||||
|
||||
// Make Verilator happy
|
||||
// Verilator lint_off UNUSED
|
||||
wire unused_formal;
|
||||
assign unused_formal = &{ 1'b0, f_next[LGFLEN], f_empty };
|
||||
// Verilator lint_on UNUSED
|
||||
`endif // FORMAL
|
||||
// }}}
|
||||
endmodule
|
||||
`ifndef YOSYS
|
||||
`default_nettype wire
|
||||
`endif
|
||||
|
|
@ -0,0 +1,495 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Filename: skidbuffer.v
|
||||
// {{{
|
||||
// Project: WB2AXIPSP: bus bridges and other odds and ends
|
||||
//
|
||||
// Purpose: A basic SKID buffer.
|
||||
// {{{
|
||||
// Skid buffers are required for high throughput AXI code, since the AXI
|
||||
// specification requires that all outputs be registered. This means
|
||||
// that, if there are any stall conditions calculated, it will take a clock
|
||||
// cycle before the stall can be propagated up stream. This means that
|
||||
// the data will need to be buffered for a cycle until the stall signal
|
||||
// can make it to the output.
|
||||
//
|
||||
// Handling that buffer is the purpose of this core.
|
||||
//
|
||||
// On one end of this core, you have the i_valid and i_data inputs to
|
||||
// connect to your bus interface. There's also a registered o_ready
|
||||
// signal to signal stalls for the bus interface.
|
||||
//
|
||||
// The other end of the core has the same basic interface, but it isn't
|
||||
// registered. This allows you to interact with the bus interfaces
|
||||
// as though they were combinatorial logic, by interacting with this half
|
||||
// of the core.
|
||||
//
|
||||
// If at any time the incoming !stall signal, i_ready, signals a stall,
|
||||
// the incoming data is placed into a buffer. Internally, that buffer
|
||||
// is held in r_data with the r_valid flag used to indicate that valid
|
||||
// data is within it.
|
||||
// }}}
|
||||
// Parameters:
|
||||
// {{{
|
||||
// DW or data width
|
||||
// In order to make this core generic, the width of the data in the
|
||||
// skid buffer is parameterized
|
||||
//
|
||||
// OPT_LOWPOWER
|
||||
// Forces both o_data and r_data to zero if the respective *VALID
|
||||
// signal is also low. While this costs extra logic, it can also
|
||||
// be used to guarantee that any unused values aren't toggling and
|
||||
// therefore unnecessarily using power.
|
||||
//
|
||||
// This excess toggling can be particularly problematic if the
|
||||
// bus signals have a high fanout rate, or a long signal path
|
||||
// across an FPGA.
|
||||
//
|
||||
// OPT_OUTREG
|
||||
// Causes the outputs to be registered
|
||||
//
|
||||
// OPT_PASSTHROUGH
|
||||
// Turns the skid buffer into a passthrough. Used for formal
|
||||
// verification only.
|
||||
// }}}
|
||||
// Creator: Dan Gisselquist, Ph.D.
|
||||
// Gisselquist Technology, LLC
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// }}}
|
||||
// Copyright (C) 2019-2024, 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 skidbuffer #(
|
||||
// {{{
|
||||
parameter [0:0] OPT_LOWPOWER = 0,
|
||||
parameter [0:0] OPT_OUTREG = 1,
|
||||
//
|
||||
parameter [0:0] OPT_PASSTHROUGH = 0,
|
||||
parameter DW = 8,
|
||||
parameter [0:0] OPT_INITIAL = 1'b1
|
||||
// }}}
|
||||
) (
|
||||
// {{{
|
||||
input wire i_clk, i_reset,
|
||||
input wire i_valid,
|
||||
output wire o_ready,
|
||||
input wire [DW-1:0] i_data,
|
||||
output wire o_valid,
|
||||
input wire i_ready,
|
||||
output reg [DW-1:0] o_data
|
||||
// }}}
|
||||
);
|
||||
|
||||
wire [DW-1:0] w_data;
|
||||
|
||||
generate if (OPT_PASSTHROUGH)
|
||||
begin : PASSTHROUGH
|
||||
// {{{
|
||||
assign { o_valid, o_ready } = { i_valid, i_ready };
|
||||
|
||||
always @(*)
|
||||
if (!i_valid && OPT_LOWPOWER)
|
||||
o_data = 0;
|
||||
else
|
||||
o_data = i_data;
|
||||
|
||||
assign w_data = 0;
|
||||
|
||||
// Keep Verilator happy
|
||||
// Verilator lint_off UNUSED
|
||||
// {{{
|
||||
wire unused_passthrough;
|
||||
assign unused_passthrough = &{ 1'b0, i_clk, i_reset };
|
||||
// }}}
|
||||
// Verilator lint_on UNUSED
|
||||
// }}}
|
||||
end else begin : LOGIC
|
||||
// We'll start with skid buffer itself
|
||||
// {{{
|
||||
reg r_valid;
|
||||
reg [DW-1:0] r_data;
|
||||
|
||||
// r_valid
|
||||
// {{{
|
||||
initial if (OPT_INITIAL) r_valid = 0;
|
||||
always @(posedge i_clk)
|
||||
if (i_reset)
|
||||
r_valid <= 0;
|
||||
else if ((i_valid && o_ready) && (o_valid && !i_ready))
|
||||
// We have incoming data, but the output is stalled
|
||||
r_valid <= 1;
|
||||
else if (i_ready)
|
||||
r_valid <= 0;
|
||||
// }}}
|
||||
|
||||
// r_data
|
||||
// {{{
|
||||
initial if (OPT_INITIAL) r_data = 0;
|
||||
always @(posedge i_clk)
|
||||
if (OPT_LOWPOWER && i_reset)
|
||||
r_data <= 0;
|
||||
else if (OPT_LOWPOWER && (!o_valid || i_ready))
|
||||
r_data <= 0;
|
||||
else if ((!OPT_LOWPOWER || !OPT_OUTREG || i_valid) && o_ready)
|
||||
r_data <= i_data;
|
||||
|
||||
assign w_data = r_data;
|
||||
// }}}
|
||||
|
||||
// o_ready
|
||||
// {{{
|
||||
assign o_ready = !r_valid;
|
||||
// }}}
|
||||
|
||||
//
|
||||
// And then move on to the output port
|
||||
//
|
||||
if (!OPT_OUTREG)
|
||||
begin : NET_OUTPUT
|
||||
// Outputs are combinatorially determined from inputs
|
||||
// {{{
|
||||
// o_valid
|
||||
// {{{
|
||||
assign o_valid = !i_reset && (i_valid || r_valid);
|
||||
// }}}
|
||||
|
||||
// o_data
|
||||
// {{{
|
||||
always @(*)
|
||||
if (r_valid)
|
||||
o_data = r_data;
|
||||
else if (!OPT_LOWPOWER || i_valid)
|
||||
o_data = i_data;
|
||||
else
|
||||
o_data = 0;
|
||||
// }}}
|
||||
// }}}
|
||||
end else begin : REG_OUTPUT
|
||||
// Register our outputs
|
||||
// {{{
|
||||
// o_valid
|
||||
// {{{
|
||||
reg ro_valid;
|
||||
|
||||
initial if (OPT_INITIAL) ro_valid = 0;
|
||||
always @(posedge i_clk)
|
||||
if (i_reset)
|
||||
ro_valid <= 0;
|
||||
else if (!o_valid || i_ready)
|
||||
ro_valid <= (i_valid || r_valid);
|
||||
|
||||
assign o_valid = ro_valid;
|
||||
// }}}
|
||||
|
||||
// o_data
|
||||
// {{{
|
||||
initial if (OPT_INITIAL) o_data = 0;
|
||||
always @(posedge i_clk)
|
||||
if (OPT_LOWPOWER && i_reset)
|
||||
o_data <= 0;
|
||||
else if (!o_valid || i_ready)
|
||||
begin
|
||||
|
||||
if (r_valid)
|
||||
o_data <= r_data;
|
||||
else if (!OPT_LOWPOWER || i_valid)
|
||||
o_data <= i_data;
|
||||
else
|
||||
o_data <= 0;
|
||||
end
|
||||
// }}}
|
||||
|
||||
// }}}
|
||||
end
|
||||
// }}}
|
||||
end endgenerate
|
||||
|
||||
// Keep Verilator happy
|
||||
// {{{
|
||||
// Verilator lint_off UNUSED
|
||||
wire unused;
|
||||
assign unused = &{ 1'b0, w_data };
|
||||
// Verilator lint_on UNUSED
|
||||
// }}}
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Formal properties
|
||||
// {{{
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
`ifdef FORMAL
|
||||
`ifdef SKIDBUFFER
|
||||
`define ASSUME assume
|
||||
`else
|
||||
`define ASSUME assert
|
||||
`endif
|
||||
|
||||
reg f_past_valid;
|
||||
|
||||
initial f_past_valid = 0;
|
||||
always @(posedge i_clk)
|
||||
f_past_valid <= 1;
|
||||
|
||||
always @(*)
|
||||
if (!f_past_valid)
|
||||
assume(i_reset);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Incoming stream properties / assumptions
|
||||
// {{{
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
always @(posedge i_clk)
|
||||
if (!f_past_valid)
|
||||
begin
|
||||
`ASSUME(!i_valid || !OPT_INITIAL);
|
||||
end else if ($past(i_valid && !o_ready && !i_reset) && !i_reset)
|
||||
`ASSUME(i_valid && $stable(i_data));
|
||||
|
||||
`ifdef VERIFIC
|
||||
`define FORMAL_VERIFIC
|
||||
// Reset properties
|
||||
property RESET_CLEARS_IVALID;
|
||||
@(posedge i_clk) i_reset |=> !i_valid;
|
||||
endproperty
|
||||
|
||||
property IDATA_HELD_WHEN_NOT_READY;
|
||||
@(posedge i_clk) disable iff (i_reset)
|
||||
i_valid && !o_ready |=> i_valid && $stable(i_data);
|
||||
endproperty
|
||||
|
||||
`ifdef SKIDBUFFER
|
||||
assume property (IDATA_HELD_WHEN_NOT_READY);
|
||||
`else
|
||||
assert property (IDATA_HELD_WHEN_NOT_READY);
|
||||
`endif
|
||||
`endif
|
||||
// }}}
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Outgoing stream properties / assumptions
|
||||
// {{{
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
|
||||
generate if (!OPT_PASSTHROUGH)
|
||||
begin
|
||||
|
||||
always @(posedge i_clk)
|
||||
if (!f_past_valid) // || $past(i_reset))
|
||||
begin
|
||||
// Following any reset, valid must be deasserted
|
||||
assert(!o_valid || !OPT_INITIAL);
|
||||
end else if ($past(o_valid && !i_ready && !i_reset) && !i_reset)
|
||||
// Following any stall, valid must remain high and
|
||||
// data must be preserved
|
||||
assert(o_valid && $stable(o_data));
|
||||
|
||||
end endgenerate
|
||||
// }}}
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Other properties
|
||||
// {{{
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
//
|
||||
generate if (!OPT_PASSTHROUGH)
|
||||
begin
|
||||
// Rule #1:
|
||||
// If registered, then following any reset we should be
|
||||
// ready for a new request
|
||||
// {{{
|
||||
always @(posedge i_clk)
|
||||
if (f_past_valid && $past(OPT_OUTREG && i_reset))
|
||||
assert(o_ready);
|
||||
// }}}
|
||||
|
||||
// Rule #2:
|
||||
// All incoming data must either go directly to the
|
||||
// output port, or into the skid buffer
|
||||
// {{{
|
||||
`ifndef VERIFIC
|
||||
always @(posedge i_clk)
|
||||
if (f_past_valid && !$past(i_reset) && $past(i_valid && o_ready
|
||||
&& (!OPT_OUTREG || o_valid) && !i_ready))
|
||||
assert(!o_ready && w_data == $past(i_data));
|
||||
`else
|
||||
assert property (@(posedge i_clk)
|
||||
disable iff (i_reset)
|
||||
(i_valid && o_ready
|
||||
&& (!OPT_OUTREG || o_valid) && !i_ready)
|
||||
|=> (!o_ready && w_data == $past(i_data)));
|
||||
`endif
|
||||
// }}}
|
||||
|
||||
// Rule #3:
|
||||
// After the last transaction, o_valid should become idle
|
||||
// {{{
|
||||
if (!OPT_OUTREG)
|
||||
begin
|
||||
// {{{
|
||||
always @(posedge i_clk)
|
||||
if (f_past_valid && !$past(i_reset) && !i_reset
|
||||
&& $past(i_ready))
|
||||
begin
|
||||
assert(o_valid == i_valid);
|
||||
assert(!i_valid || (o_data == i_data));
|
||||
end
|
||||
// }}}
|
||||
end else begin
|
||||
// {{{
|
||||
always @(posedge i_clk)
|
||||
if (f_past_valid && !$past(i_reset))
|
||||
begin
|
||||
if ($past(i_valid && o_ready))
|
||||
assert(o_valid);
|
||||
|
||||
if ($past(!i_valid && o_ready && i_ready))
|
||||
assert(!o_valid);
|
||||
end
|
||||
// }}}
|
||||
end
|
||||
// }}}
|
||||
|
||||
// Rule #4
|
||||
// Same thing, but this time for o_ready
|
||||
// {{{
|
||||
always @(posedge i_clk)
|
||||
if (f_past_valid && $past(!o_ready && i_ready))
|
||||
assert(o_ready);
|
||||
// }}}
|
||||
|
||||
// If OPT_LOWPOWER is set, o_data and w_data both need to be
|
||||
// zero any time !o_valid or !r_valid respectively
|
||||
// {{{
|
||||
if (OPT_LOWPOWER)
|
||||
begin
|
||||
always @(*)
|
||||
if ((OPT_OUTREG || !i_reset) && !o_valid)
|
||||
assert(o_data == 0);
|
||||
|
||||
always @(*)
|
||||
if (o_ready)
|
||||
assert(w_data == 0);
|
||||
|
||||
end
|
||||
// }}}
|
||||
end endgenerate
|
||||
// }}}
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Cover checks
|
||||
// {{{
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
//
|
||||
`ifdef SKIDBUFFER
|
||||
generate if (!OPT_PASSTHROUGH)
|
||||
begin
|
||||
reg f_changed_data;
|
||||
|
||||
initial f_changed_data = 0;
|
||||
always @(posedge i_clk)
|
||||
if (i_reset)
|
||||
f_changed_data <= 1;
|
||||
else if (i_valid && $past(!i_valid || o_ready))
|
||||
begin
|
||||
if (i_data != $past(i_data + 1))
|
||||
f_changed_data <= 0;
|
||||
end else if (!i_valid && i_data != 0)
|
||||
f_changed_data <= 0;
|
||||
|
||||
|
||||
`ifndef VERIFIC
|
||||
reg [3:0] cvr_steps, cvr_hold;
|
||||
|
||||
always @(posedge i_clk)
|
||||
if (i_reset)
|
||||
begin
|
||||
cvr_steps <= 0;
|
||||
cvr_hold <= 0;
|
||||
end else begin
|
||||
cvr_steps <= cvr_steps + 1;
|
||||
cvr_hold <= cvr_hold + 1;
|
||||
case(cvr_steps)
|
||||
0: if (o_valid || i_valid)
|
||||
cvr_steps <= 0;
|
||||
1: if (!i_valid || !i_ready)
|
||||
cvr_steps <= 0;
|
||||
2: if (!i_valid || !i_ready)
|
||||
cvr_steps <= 0;
|
||||
3: if (!i_valid || !i_ready)
|
||||
cvr_steps <= 0;
|
||||
4: if (!i_valid || i_ready)
|
||||
cvr_steps <= 0;
|
||||
5: if (!i_valid || !i_ready)
|
||||
cvr_steps <= 0;
|
||||
6: if (!i_valid || !i_ready)
|
||||
cvr_steps <= 0;
|
||||
7: if (!i_valid || i_ready)
|
||||
cvr_steps <= 0;
|
||||
8: if (!i_valid || i_ready)
|
||||
cvr_steps <= 0;
|
||||
9: if (!i_valid || !i_ready)
|
||||
cvr_steps <= 0;
|
||||
10: if (!i_valid || !i_ready)
|
||||
cvr_steps <= 0;
|
||||
11: if (!i_valid || !i_ready)
|
||||
cvr_steps <= 0;
|
||||
12: begin
|
||||
cvr_steps <= cvr_steps;
|
||||
cover(!o_valid && !i_valid && f_changed_data);
|
||||
if (!o_valid || !i_ready)
|
||||
cvr_steps <= 0;
|
||||
else
|
||||
cvr_hold <= cvr_hold + 1;
|
||||
end
|
||||
default: assert(0);
|
||||
endcase
|
||||
end
|
||||
|
||||
`else
|
||||
// Cover test
|
||||
cover property (@(posedge i_clk)
|
||||
disable iff (i_reset)
|
||||
(!o_valid && !i_valid)
|
||||
##1 i_valid && i_ready [*3]
|
||||
##1 i_valid && !i_ready
|
||||
##1 i_valid && i_ready [*2]
|
||||
##1 i_valid && !i_ready [*2]
|
||||
##1 i_valid && i_ready [*3]
|
||||
// Wait for the design to clear
|
||||
##1 o_valid && i_ready [*0:5]
|
||||
##1 (!o_valid && !i_valid && f_changed_data));
|
||||
`endif
|
||||
end endgenerate
|
||||
`endif // SKIDBUFFER
|
||||
// }}}
|
||||
`endif
|
||||
// }}}
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,404 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Filename: wbarbiter.v
|
||||
// {{{
|
||||
// Project: WB2AXIPSP: bus bridges and other odds and ends
|
||||
//
|
||||
// Purpose: This is a priority bus arbiter. It allows two separate wishbone
|
||||
// masters to connect to the same bus, while also guaranteeing
|
||||
// that the last master can have the bus with no delay any time it is
|
||||
// idle. The goal is to minimize the combinatorial logic required in this
|
||||
// process, while still minimizing access time.
|
||||
//
|
||||
// The core logic works like this:
|
||||
//
|
||||
// 1. If 'A' or 'B' asserts the o_cyc line, a bus cycle will begin,
|
||||
// with acccess granted to whomever requested it.
|
||||
// 2. If both 'A' and 'B' assert o_cyc at the same time, only 'A'
|
||||
// will be granted the bus. (If the alternating parameter
|
||||
// is set, A and B will alternate who gets the bus in
|
||||
// this case.)
|
||||
// 3. The bus will remain owned by whomever the bus was granted to
|
||||
// until they deassert the o_cyc line.
|
||||
// 4. At the end of a bus cycle, o_cyc is guaranteed to be
|
||||
// deasserted (low) for one clock.
|
||||
// 5. On the next clock, bus arbitration takes place again. If
|
||||
// 'A' requests the bus, no matter how long 'B' was
|
||||
// waiting, 'A' will then be granted the bus. (Unless
|
||||
// again the alternating parameter is set, then the
|
||||
// access is guaranteed to switch to B.)
|
||||
//
|
||||
//
|
||||
// Creator: Dan Gisselquist, Ph.D.
|
||||
// Gisselquist Technology, LLC
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// }}}
|
||||
// Copyright (C) 2015-2024, 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
|
||||
//
|
||||
`define WBA_ALTERNATING
|
||||
// }}}
|
||||
module wbarbiter #(
|
||||
// {{{
|
||||
parameter DW=32, AW=32,
|
||||
parameter SCHEME="ALTERNATING",
|
||||
parameter [0:0] OPT_ZERO_ON_IDLE = 1'b0,
|
||||
parameter [31:0] F_MAX_STALL = 3,
|
||||
parameter [31:0] F_MAX_ACK_DELAY = 3,
|
||||
parameter [31:0] F_LGDEPTH=3
|
||||
// }}}
|
||||
) (
|
||||
// {{{
|
||||
input wire i_clk, i_reset,
|
||||
// Bus A
|
||||
// {{{
|
||||
input wire i_a_cyc, i_a_stb, i_a_we,
|
||||
input wire [(AW-1):0] i_a_adr,
|
||||
input wire [(DW-1):0] i_a_dat,
|
||||
input wire [(DW/8-1):0] i_a_sel,
|
||||
output wire o_a_ack, o_a_stall, o_a_err,
|
||||
// }}}
|
||||
// Bus B
|
||||
// {{{
|
||||
input wire i_b_cyc, i_b_stb, i_b_we,
|
||||
input wire [(AW-1):0] i_b_adr,
|
||||
input wire [(DW-1):0] i_b_dat,
|
||||
input wire [(DW/8-1):0] i_b_sel,
|
||||
output wire o_b_ack, o_b_stall, o_b_err,
|
||||
// }}}
|
||||
// Combined/arbitrated bus
|
||||
// {{{
|
||||
output wire o_cyc, o_stb, o_we,
|
||||
output wire [(AW-1):0] o_adr,
|
||||
output wire [(DW-1):0] o_dat,
|
||||
output wire [(DW/8-1):0] o_sel,
|
||||
input wire i_ack, i_stall, i_err
|
||||
// }}}
|
||||
`ifdef FORMAL
|
||||
// {{{
|
||||
,
|
||||
output wire [(F_LGDEPTH-1):0]
|
||||
f_nreqs, f_nacks, f_outstanding,
|
||||
f_a_nreqs, f_a_nacks, f_a_outstanding,
|
||||
f_b_nreqs, f_b_nacks, f_b_outstanding
|
||||
// }}}
|
||||
`endif
|
||||
// }}}
|
||||
);
|
||||
//
|
||||
|
||||
// Go high immediately (new cycle) if ...
|
||||
// Previous cycle was low and *someone* is requesting a bus cycle
|
||||
// Go low immadiately if ...
|
||||
// We were just high and the owner no longer wants the bus
|
||||
// WISHBONE Spec recommends no logic between a FF and the o_cyc
|
||||
// This violates that spec. (Rec 3.15, p35)
|
||||
reg r_a_owner;
|
||||
|
||||
assign o_cyc = (r_a_owner) ? i_a_cyc : i_b_cyc;
|
||||
initial r_a_owner = 1'b1;
|
||||
|
||||
generate if (SCHEME == "PRIORITY")
|
||||
begin : PRI
|
||||
|
||||
always @(posedge i_clk)
|
||||
if (!i_b_cyc)
|
||||
r_a_owner <= 1'b1;
|
||||
// Allow B to set its CYC line w/o activating this
|
||||
// interface
|
||||
else if ((i_b_stb)&&(!i_a_cyc))
|
||||
r_a_owner <= 1'b0;
|
||||
|
||||
end else if (SCHEME == "ALTERNATING")
|
||||
begin : ALT
|
||||
|
||||
reg last_owner;
|
||||
initial last_owner = 1'b0;
|
||||
always @(posedge i_clk)
|
||||
if ((i_a_cyc)&&(r_a_owner))
|
||||
last_owner <= 1'b1;
|
||||
else if ((i_b_cyc)&&(!r_a_owner))
|
||||
last_owner <= 1'b0;
|
||||
|
||||
always @(posedge i_clk)
|
||||
if ((!i_a_cyc)&&(!i_b_cyc))
|
||||
r_a_owner <= !last_owner;
|
||||
else if ((r_a_owner)&&(!i_a_cyc))
|
||||
begin
|
||||
|
||||
if (i_b_stb)
|
||||
r_a_owner <= 1'b0;
|
||||
|
||||
end else if ((!r_a_owner)&&(!i_b_cyc))
|
||||
begin
|
||||
|
||||
if (i_a_stb)
|
||||
r_a_owner <= 1'b1;
|
||||
|
||||
end
|
||||
|
||||
end else // if (SCHEME == "LAST")
|
||||
begin : LST
|
||||
always @(posedge i_clk)
|
||||
if ((!i_a_cyc)&&(i_b_stb))
|
||||
r_a_owner <= 1'b0;
|
||||
else if ((!i_b_cyc)&&(i_a_stb))
|
||||
r_a_owner <= 1'b1;
|
||||
end endgenerate
|
||||
|
||||
|
||||
// Realistically, if neither master owns the bus, the output is a
|
||||
// don't care. Thus we trigger off whether or not 'A' owns the bus.
|
||||
// If 'B' owns it all we care is that 'A' does not. Likewise, if
|
||||
// neither owns the bus than the values on the various lines are
|
||||
// irrelevant.
|
||||
assign o_we = (r_a_owner) ? i_a_we : i_b_we;
|
||||
|
||||
generate if (OPT_ZERO_ON_IDLE)
|
||||
begin : ZERO_IDLE
|
||||
// {{{
|
||||
//
|
||||
// OPT_ZERO_ON_IDLE will use up more logic and may even slow
|
||||
// down the master clock if set. However, it may also reduce
|
||||
// the power used by the FPGA by preventing things from toggling
|
||||
// when the bus isn't in use. The option is here because it
|
||||
// also makes it a lot easier to look for when things happen
|
||||
// on the bus via VERILATOR when timing and logic counts
|
||||
// don't matter.
|
||||
//
|
||||
assign o_stb = (o_cyc)? ((r_a_owner) ? i_a_stb : i_b_stb):0;
|
||||
assign o_adr = (o_stb)? ((r_a_owner) ? i_a_adr : i_b_adr):0;
|
||||
assign o_dat = (o_stb)? ((r_a_owner) ? i_a_dat : i_b_dat):0;
|
||||
assign o_sel = (o_stb)? ((r_a_owner) ? i_a_sel : i_b_sel):0;
|
||||
assign o_a_ack = (o_cyc)&&( r_a_owner) ? i_ack : 1'b0;
|
||||
assign o_b_ack = (o_cyc)&&(!r_a_owner) ? i_ack : 1'b0;
|
||||
assign o_a_stall = (o_cyc)&&( r_a_owner) ? i_stall : 1'b1;
|
||||
assign o_b_stall = (o_cyc)&&(!r_a_owner) ? i_stall : 1'b1;
|
||||
assign o_a_err = (o_cyc)&&( r_a_owner) ? i_err : 1'b0;
|
||||
assign o_b_err = (o_cyc)&&(!r_a_owner) ? i_err : 1'b0;
|
||||
// }}}
|
||||
end else begin : LOW_LOGIC
|
||||
// {{{
|
||||
assign o_stb = (r_a_owner) ? i_a_stb : i_b_stb;
|
||||
assign o_adr = (r_a_owner) ? i_a_adr : i_b_adr;
|
||||
assign o_dat = (r_a_owner) ? i_a_dat : i_b_dat;
|
||||
assign o_sel = (r_a_owner) ? i_a_sel : i_b_sel;
|
||||
|
||||
// We cannot allow the return acknowledgement to ever go high if
|
||||
// the master in question does not own the bus. Hence we force
|
||||
// it low if the particular master doesn't own the bus.
|
||||
assign o_a_ack = ( r_a_owner) ? i_ack : 1'b0;
|
||||
assign o_b_ack = (!r_a_owner) ? i_ack : 1'b0;
|
||||
|
||||
// Stall must be asserted on the same cycle the input master
|
||||
// asserts the bus, if the bus isn't granted to him.
|
||||
assign o_a_stall = ( r_a_owner) ? i_stall : 1'b1;
|
||||
assign o_b_stall = (!r_a_owner) ? i_stall : 1'b1;
|
||||
|
||||
//
|
||||
//
|
||||
assign o_a_err = ( r_a_owner) ? i_err : 1'b0;
|
||||
assign o_b_err = (!r_a_owner) ? i_err : 1'b0;
|
||||
// }}}
|
||||
end endgenerate
|
||||
|
||||
// Make Verilator happy
|
||||
// {{{
|
||||
// verilator lint_off UNUSED
|
||||
wire unused;
|
||||
assign unused = &{ 1'b0, i_reset, F_LGDEPTH, F_MAX_STALL,
|
||||
F_MAX_ACK_DELAY };
|
||||
// verilator lint_on UNUSED
|
||||
// }}}
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Formal properties
|
||||
// {{{
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
`ifdef FORMAL
|
||||
|
||||
`ifdef WBARBITER
|
||||
|
||||
`define ASSUME assume
|
||||
`else
|
||||
`define ASSUME assert
|
||||
`endif
|
||||
reg f_prior_a_ack, f_prior_b_ack;
|
||||
|
||||
|
||||
reg f_past_valid;
|
||||
initial f_past_valid = 1'b0;
|
||||
always @(posedge i_clk)
|
||||
f_past_valid <= 1'b1;
|
||||
|
||||
initial `ASSUME(!i_a_cyc);
|
||||
initial `ASSUME(!i_a_stb);
|
||||
|
||||
initial `ASSUME(!i_b_cyc);
|
||||
initial `ASSUME(!i_b_stb);
|
||||
|
||||
initial `ASSUME(!i_ack);
|
||||
initial `ASSUME(!i_err);
|
||||
|
||||
always @(*)
|
||||
if (!f_past_valid)
|
||||
`ASSUME(i_reset);
|
||||
|
||||
always @(posedge i_clk)
|
||||
begin
|
||||
if (o_cyc)
|
||||
assert((i_a_cyc)||(i_b_cyc));
|
||||
if ((f_past_valid)&&($past(o_cyc))&&(o_cyc))
|
||||
assert($past(r_a_owner) == r_a_owner);
|
||||
end
|
||||
|
||||
fwb_master #(
|
||||
// {{{
|
||||
.DW(DW), .AW(AW),
|
||||
.F_MAX_STALL(F_MAX_STALL),
|
||||
.F_LGDEPTH(F_LGDEPTH),
|
||||
.F_MAX_ACK_DELAY(F_MAX_ACK_DELAY),
|
||||
.F_OPT_RMW_BUS_OPTION(1),
|
||||
.F_OPT_DISCONTINUOUS(1)
|
||||
// }}}
|
||||
) f_wbm(
|
||||
// {{{
|
||||
i_clk, i_reset,
|
||||
o_cyc, o_stb, o_we, o_adr, o_dat, o_sel,
|
||||
i_ack, i_stall, 32'h0, i_err,
|
||||
f_nreqs, f_nacks, f_outstanding
|
||||
// }}}
|
||||
);
|
||||
|
||||
fwb_slave #(
|
||||
// {{{
|
||||
.DW(DW), .AW(AW),
|
||||
.F_MAX_STALL(0),
|
||||
.F_LGDEPTH(F_LGDEPTH),
|
||||
.F_MAX_ACK_DELAY(0),
|
||||
.F_OPT_RMW_BUS_OPTION(1),
|
||||
.F_OPT_DISCONTINUOUS(1)
|
||||
// }}}
|
||||
) f_wba(
|
||||
// {{{
|
||||
i_clk, i_reset,
|
||||
i_a_cyc, i_a_stb, i_a_we, i_a_adr, i_a_dat, i_a_sel,
|
||||
o_a_ack, o_a_stall, 32'h0, o_a_err,
|
||||
f_a_nreqs, f_a_nacks, f_a_outstanding
|
||||
// }}}
|
||||
);
|
||||
|
||||
fwb_slave #(
|
||||
// {{{
|
||||
.DW(DW), .AW(AW),
|
||||
.F_MAX_STALL(0),
|
||||
.F_LGDEPTH(F_LGDEPTH),
|
||||
.F_MAX_ACK_DELAY(0),
|
||||
.F_OPT_RMW_BUS_OPTION(1),
|
||||
.F_OPT_DISCONTINUOUS(1)
|
||||
// }}}
|
||||
) f_wbb(
|
||||
// {{{
|
||||
i_clk, i_reset,
|
||||
i_b_cyc, i_b_stb, i_b_we, i_b_adr, i_b_dat, i_b_sel,
|
||||
o_b_ack, o_b_stall, 32'h0, o_b_err,
|
||||
f_b_nreqs, f_b_nacks, f_b_outstanding
|
||||
// }}}
|
||||
);
|
||||
|
||||
always @(posedge i_clk)
|
||||
if (r_a_owner)
|
||||
begin
|
||||
assert(f_b_nreqs == 0);
|
||||
assert(f_b_nacks == 0);
|
||||
assert(f_a_outstanding == f_outstanding);
|
||||
end else begin
|
||||
assert(f_a_nreqs == 0);
|
||||
assert(f_a_nacks == 0);
|
||||
assert(f_b_outstanding == f_outstanding);
|
||||
end
|
||||
|
||||
always @(posedge i_clk)
|
||||
if ((f_past_valid)&&(!$past(i_reset))
|
||||
&&($past(i_a_stb))&&(!$past(i_b_cyc)))
|
||||
assert(r_a_owner);
|
||||
|
||||
always @(posedge i_clk)
|
||||
if ((f_past_valid)&&(!$past(i_reset))
|
||||
&&(!$past(i_a_cyc))&&($past(i_b_stb)))
|
||||
assert(!r_a_owner);
|
||||
|
||||
always @(posedge i_clk)
|
||||
if ((f_past_valid)&&(r_a_owner != $past(r_a_owner)))
|
||||
assert(!$past(o_cyc));
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Cover checks
|
||||
// {{{
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
//
|
||||
initial f_prior_a_ack = 1'b0;
|
||||
always @(posedge i_clk)
|
||||
if ((i_reset)||(o_a_err)||(o_b_err))
|
||||
f_prior_a_ack <= 1'b0;
|
||||
else if ((o_cyc)&&(o_a_ack))
|
||||
f_prior_a_ack <= 1'b1;
|
||||
|
||||
initial f_prior_b_ack = 1'b0;
|
||||
always @(posedge i_clk)
|
||||
if ((i_reset)||(o_a_err)||(o_b_err))
|
||||
f_prior_b_ack <= 1'b0;
|
||||
else if ((o_cyc)&&(o_b_ack))
|
||||
f_prior_b_ack <= 1'b1;
|
||||
|
||||
always @(posedge i_clk)
|
||||
begin
|
||||
cover(f_prior_b_ack && o_cyc && o_a_ack);
|
||||
|
||||
cover((o_cyc && o_a_ack)
|
||||
&&($past(o_cyc && o_a_ack))
|
||||
&&($past(o_cyc && o_a_ack,2)));
|
||||
|
||||
|
||||
cover(f_prior_a_ack && o_cyc && o_b_ack);
|
||||
|
||||
cover((o_cyc && o_b_ack)
|
||||
&&($past(o_cyc && o_b_ack))
|
||||
&&($past(o_cyc && o_b_ack,2)));
|
||||
end
|
||||
|
||||
always @(*)
|
||||
cover(o_cyc && o_b_ack);
|
||||
// }}}
|
||||
// }}}
|
||||
`endif
|
||||
endmodule
|
||||
`ifndef YOSYS
|
||||
`default_nettype wire
|
||||
`endif
|
||||
|
|
@ -0,0 +1,282 @@
|
|||
module ddr3_axi_tb;
|
||||
|
||||
reg i_controller_clk, i_ddr3_clk, i_ref_clk, i_ddr3_clk_90;
|
||||
reg i_rst_n;
|
||||
|
||||
// AXI Interface
|
||||
// AXI write address channel signals
|
||||
reg s_axi_awvalid;
|
||||
wire s_axi_awready;
|
||||
reg [dut.AXI_ID_WIDTH-1:0] s_axi_awid;
|
||||
reg [dut.AXI_ADDR_WIDTH-1:0] s_axi_awaddr;
|
||||
// AXI write data channel signals
|
||||
reg s_axi_wvalid;
|
||||
wire s_axi_wready;
|
||||
reg [dut.AXI_DATA_WIDTH-1:0] s_axi_wdata;
|
||||
reg [dut.AXI_DATA_WIDTH/8-1:0] s_axi_wstrb;
|
||||
reg s_axi_wlast;
|
||||
// AXI write response channel signals
|
||||
wire s_axi_bvalid;
|
||||
reg s_axi_bready;
|
||||
wire [dut.AXI_ID_WIDTH-1:0] s_axi_bid;
|
||||
wire [1:0] s_axi_bresp;
|
||||
// AXI read address channel signals
|
||||
reg s_axi_arvalid;
|
||||
wire s_axi_arready;
|
||||
reg [dut.AXI_ID_WIDTH-1:0] s_axi_arid;
|
||||
reg [dut.AXI_ADDR_WIDTH-1:0] s_axi_araddr;
|
||||
// AXI read data channel signals
|
||||
wire s_axi_rvalid; // rd rslt valid
|
||||
reg s_axi_rready; // rd rslt ready
|
||||
wire [dut.AXI_ID_WIDTH-1:0] s_axi_rid; // response id
|
||||
wire [dut.AXI_DATA_WIDTH-1:0] s_axi_rdata;// read data
|
||||
wire s_axi_rlast; // read last
|
||||
wire [1:0] s_axi_rresp; // read response
|
||||
|
||||
// DDR3 Pins
|
||||
wire o_ddr3_clk_p;
|
||||
wire o_ddr3_clk_n;
|
||||
wire o_ddr3_reset_n;
|
||||
wire o_ddr3_cke;
|
||||
wire o_ddr3_cs_n;
|
||||
wire o_ddr3_ras_n;
|
||||
wire o_ddr3_cas_n;
|
||||
wire o_ddr3_we_n;
|
||||
wire[dut.ROW_BITS-1:0] o_ddr3_addr;
|
||||
wire[dut.BA_BITS-1:0] o_ddr3_ba_addr;
|
||||
wire[(dut.DQ_BITS*dut.BYTE_LANES)-1:0] io_ddr3_dq;
|
||||
wire[dut.BYTE_LANES-1:0] io_ddr3_dqs, io_ddr3_dqs_n;
|
||||
wire[dut.BYTE_LANES-1:0] o_ddr3_dm;
|
||||
wire o_ddr3_odt;
|
||||
|
||||
localparam CONTROLLER_CLK_PERIOD = 10_000, //ps, period of clock input to this DDR3 controller module
|
||||
DDR3_CLK_PERIOD = 2500; //ps, period of clock input to DDR3 RAM device
|
||||
|
||||
|
||||
// Clocks and reset
|
||||
always #(CONTROLLER_CLK_PERIOD/2) i_controller_clk = !i_controller_clk;
|
||||
always #(DDR3_CLK_PERIOD/2) i_ddr3_clk = !i_ddr3_clk;
|
||||
always #2500 i_ref_clk = !i_ref_clk;
|
||||
initial begin //90 degree phase shifted ddr3_clk
|
||||
#(DDR3_CLK_PERIOD/4);
|
||||
while(1) begin
|
||||
#(DDR3_CLK_PERIOD/2) i_ddr3_clk_90 = !i_ddr3_clk_90;
|
||||
end
|
||||
end
|
||||
initial begin
|
||||
i_controller_clk = 1;
|
||||
i_ddr3_clk = 1;
|
||||
i_ref_clk = 1;
|
||||
i_ddr3_clk_90 = 1;
|
||||
i_rst_n = 0;
|
||||
#1_000_000;
|
||||
i_rst_n = 1;
|
||||
end
|
||||
|
||||
initial begin
|
||||
// initialize AXI
|
||||
s_axi_awvalid = 0;
|
||||
s_axi_awid = 0;
|
||||
s_axi_awaddr = 0;
|
||||
|
||||
s_axi_wvalid = 0;
|
||||
s_axi_wdata = 0;
|
||||
s_axi_wstrb = 0;
|
||||
s_axi_wlast = 0;
|
||||
|
||||
s_axi_bready = 0;
|
||||
|
||||
s_axi_arvalid = 0;
|
||||
s_axi_arid = 0;
|
||||
s_axi_araddr = 0;
|
||||
|
||||
s_axi_rready = 0;
|
||||
|
||||
//wait until done calibrate
|
||||
wait(dut.ddr3_top_inst.ddr3_controller_inst.state_calibrate == 23);
|
||||
|
||||
// write data to address 3 (0-15 = address 0, 16-31 = address 1, 32-47 = address 2)
|
||||
@(negedge i_controller_clk);
|
||||
s_axi_awvalid = 1;
|
||||
s_axi_awid = 0;
|
||||
s_axi_awaddr = 33;
|
||||
@(negedge i_controller_clk);
|
||||
// while(!s_axi_awready) begin
|
||||
// @(negedge i_controller_clk);
|
||||
// end
|
||||
s_axi_awvalid = 0;
|
||||
s_axi_awid = 0;
|
||||
s_axi_awaddr = 0;
|
||||
|
||||
s_axi_wvalid = 1;
|
||||
s_axi_wdata = 128'hAAAA_BBBB_CCCC_DDDD_EEEE_FFFF_0000_1111; // data 1
|
||||
s_axi_wstrb = -1;
|
||||
|
||||
@(negedge i_controller_clk);
|
||||
while(!s_axi_wready) begin
|
||||
@(negedge i_controller_clk);
|
||||
end
|
||||
s_axi_wdata = 128'h2222_3333_4444_5555_6666_7777_8888_9999; // data 2
|
||||
|
||||
@(negedge i_controller_clk);
|
||||
while(!s_axi_wready) begin
|
||||
@(negedge i_controller_clk);
|
||||
end
|
||||
s_axi_wdata = 100; // data 3
|
||||
|
||||
@(negedge i_controller_clk);
|
||||
while(!s_axi_wready) begin
|
||||
@(negedge i_controller_clk);
|
||||
end
|
||||
s_axi_wdata = 2000; // data 4
|
||||
s_axi_wlast = 1;
|
||||
|
||||
@(negedge i_controller_clk);
|
||||
while(!s_axi_wready) begin
|
||||
@(negedge i_controller_clk);
|
||||
end
|
||||
s_axi_wvalid = 0;
|
||||
s_axi_wdata = 0;
|
||||
s_axi_wstrb = 0;
|
||||
s_axi_wlast = 0;
|
||||
// DONE write data to address 3
|
||||
|
||||
// wait for write response
|
||||
wait(s_axi_bvalid);
|
||||
@(negedge i_controller_clk);
|
||||
s_axi_bready = 1;
|
||||
@(negedge i_controller_clk);
|
||||
s_axi_bready = 0;
|
||||
// done waiting for write response
|
||||
|
||||
#1000_000;
|
||||
// read data request from address 2 (0-15 = address 0, 16-31 = address 1, 32-47 = address 2)
|
||||
@(negedge i_controller_clk);
|
||||
s_axi_arvalid = 1;
|
||||
s_axi_arid = 2;
|
||||
s_axi_araddr = 46;
|
||||
@(negedge i_controller_clk);
|
||||
s_axi_arvalid = 0;
|
||||
s_axi_arid = 0;
|
||||
s_axi_araddr = 0;
|
||||
// done read data request from address 3
|
||||
|
||||
// wait for read data
|
||||
wait(s_axi_rvalid);
|
||||
@(negedge i_controller_clk);
|
||||
@(negedge i_controller_clk);
|
||||
@(negedge i_controller_clk);
|
||||
@(negedge i_controller_clk);
|
||||
s_axi_rready = 1;
|
||||
@(negedge i_controller_clk);
|
||||
@(negedge i_controller_clk);
|
||||
@(negedge i_controller_clk);
|
||||
@(negedge i_controller_clk);
|
||||
s_axi_rready = 0;
|
||||
#1000_000;
|
||||
$finish;
|
||||
|
||||
end
|
||||
ddr3_top_axi #(
|
||||
.CONTROLLER_CLK_PERIOD(10_000), //ps, clock period of the controller interface
|
||||
.DDR3_CLK_PERIOD(2_500), //ps, clock period of the DDR3 RAM device (must be 1/4 of the CONTROLLER_CLK_PERIOD)
|
||||
.ROW_BITS(14), //width of row address
|
||||
.COL_BITS(10), //width of column address
|
||||
.BA_BITS(3), //width of bank address
|
||||
.BYTE_LANES(2), //number of byte lanes of DDR3 RAM
|
||||
.AXI_ID_WIDTH(4), // The AXI id width used for R&W, an int between 1-16
|
||||
.MICRON_SIM(1), //enable faster simulation for micron ddr3 model (shorten POWER_ON_RESET_HIGH and INITIAL_CKE_LOW)
|
||||
.ODELAY_SUPPORTED(0), //set to 1 when ODELAYE2 is supported
|
||||
.SECOND_WISHBONE(0) //set to 1 if 2nd wishbone for debugging is needed
|
||||
) dut
|
||||
(
|
||||
.i_controller_clk(i_controller_clk),
|
||||
.i_ddr3_clk(i_ddr3_clk),
|
||||
.i_ref_clk(i_ref_clk), //i_controller_clk = CONTROLLER_CLK_PERIOD, i_ddr3_clk = DDR3_CLK_PERIOD, i_ref_clk = 200MHz
|
||||
.i_ddr3_clk_90(i_ddr3_clk_90), //required only when ODELAY_SUPPORTED is zero
|
||||
.i_rst_n(i_rst_n),
|
||||
//
|
||||
// AXI Interface
|
||||
// AXI write address channel signals
|
||||
.s_axi_awvalid(s_axi_awvalid),
|
||||
.s_axi_awready(s_axi_awready),
|
||||
.s_axi_awid(s_axi_awid),
|
||||
.s_axi_awaddr(s_axi_awaddr),
|
||||
.s_axi_awlen(3), // 4 transfers in a transaction
|
||||
.s_axi_awsize($clog2(128)),
|
||||
.s_axi_awburst(1), //incrementing burst address
|
||||
.s_axi_awlock(0),
|
||||
.s_axi_awcache(0),
|
||||
.s_axi_awprot(0),
|
||||
.s_axi_awqos(0),
|
||||
// AXI write data channel signals
|
||||
.s_axi_wvalid(s_axi_wvalid),
|
||||
.s_axi_wready(s_axi_wready),
|
||||
.s_axi_wdata(s_axi_wdata),
|
||||
.s_axi_wstrb(s_axi_wstrb),
|
||||
.s_axi_wlast(s_axi_wlast),
|
||||
// AXI write response channel signals
|
||||
.s_axi_bvalid(s_axi_bvalid),
|
||||
.s_axi_bready(s_axi_bready),
|
||||
.s_axi_bid(s_axi_bid),
|
||||
.s_axi_bresp(s_axi_bresp),
|
||||
// AXI read address channel signals
|
||||
.s_axi_arvalid(s_axi_arvalid),
|
||||
.s_axi_arready(s_axi_arready),
|
||||
.s_axi_arid(s_axi_arid),
|
||||
.s_axi_araddr(s_axi_araddr),
|
||||
.s_axi_arlen(3), // only 1 transfer in a transaction
|
||||
.s_axi_arsize($clog2(128)),
|
||||
.s_axi_arburst(1), //incrementing burst address
|
||||
.s_axi_arlock(0),
|
||||
.s_axi_arcache(0),
|
||||
.s_axi_arprot(0),
|
||||
.s_axi_arqos(0),
|
||||
// AXI read data channel signals
|
||||
.s_axi_rvalid(s_axi_rvalid), // rd rslt valid
|
||||
.s_axi_rready(s_axi_rready), // rd rslt ready
|
||||
.s_axi_rid(s_axi_rid), // response id
|
||||
.s_axi_rdata(s_axi_rdata),// read data
|
||||
.s_axi_rlast(s_axi_rlast), // read last
|
||||
.s_axi_rresp(s_axi_rresp), // read response
|
||||
//
|
||||
// DDR3 I/O Interface
|
||||
.o_ddr3_clk_p(o_ddr3_clk_p),
|
||||
.o_ddr3_clk_n(o_ddr3_clk_n),
|
||||
.o_ddr3_reset_n(o_ddr3_reset_n),
|
||||
.o_ddr3_cke(o_ddr3_cke),
|
||||
.o_ddr3_cs_n(o_ddr3_cs_n),
|
||||
.o_ddr3_ras_n(o_ddr3_ras_n),
|
||||
.o_ddr3_cas_n(o_ddr3_cas_n),
|
||||
.o_ddr3_we_n(o_ddr3_we_n),
|
||||
.o_ddr3_addr(o_ddr3_addr),
|
||||
.o_ddr3_ba_addr(o_ddr3_ba_addr),
|
||||
.io_ddr3_dq(io_ddr3_dq),
|
||||
.io_ddr3_dqs(io_ddr3_dqs),
|
||||
.io_ddr3_dqs_n(io_ddr3_dqs_n),
|
||||
.o_ddr3_dm(o_ddr3_dm),
|
||||
.o_ddr3_odt(o_ddr3_odt)
|
||||
//
|
||||
);
|
||||
|
||||
ddr3 ddr3_0(
|
||||
.rst_n(o_ddr3_reset_n),
|
||||
.ck(o_ddr3_clk_p),
|
||||
.ck_n(o_ddr3_clk_n),
|
||||
.cke(o_ddr3_cke),
|
||||
.cs_n(o_ddr3_cs_n),
|
||||
.ras_n(o_ddr3_ras_n),
|
||||
.cas_n(o_ddr3_cas_n),
|
||||
.we_n(o_ddr3_we_n),
|
||||
.dm_tdqs(o_ddr3_dm),
|
||||
.ba(o_ddr3_ba_addr),
|
||||
.addr({0,o_ddr3_addr}),
|
||||
.dq(io_ddr3_dq),
|
||||
.dqs(io_ddr3_dqs),
|
||||
.dqs_n(io_ddr3_dqs_n),
|
||||
.tdqs_n(),
|
||||
.odt(o_ddr3_odt)
|
||||
);
|
||||
|
||||
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,202 @@
|
|||
module ddr3_axi_traffic_gen;
|
||||
|
||||
reg i_controller_clk, i_ddr3_clk, i_ref_clk, i_ddr3_clk_90;
|
||||
reg i_rst_n;
|
||||
|
||||
// DDR3 Pins
|
||||
wire o_ddr3_clk_p;
|
||||
wire o_ddr3_clk_n;
|
||||
wire o_ddr3_reset_n;
|
||||
wire o_ddr3_cke;
|
||||
wire o_ddr3_cs_n;
|
||||
wire o_ddr3_ras_n;
|
||||
wire o_ddr3_cas_n;
|
||||
wire o_ddr3_we_n;
|
||||
wire[dut.ROW_BITS-1:0] o_ddr3_addr;
|
||||
wire[dut.BA_BITS-1:0] o_ddr3_ba_addr;
|
||||
wire[(dut.DQ_BITS*dut.BYTE_LANES)-1:0] io_ddr3_dq;
|
||||
wire[dut.BYTE_LANES-1:0] io_ddr3_dqs, io_ddr3_dqs_n;
|
||||
wire[dut.BYTE_LANES-1:0] o_ddr3_dm;
|
||||
wire o_ddr3_odt;
|
||||
|
||||
localparam CONTROLLER_CLK_PERIOD = 10_000, //ps, period of clock input to this DDR3 controller module
|
||||
DDR3_CLK_PERIOD = 2500; //ps, period of clock input to DDR3 RAM device
|
||||
|
||||
|
||||
// Clocks and reset
|
||||
always #(CONTROLLER_CLK_PERIOD/2) i_controller_clk = !i_controller_clk;
|
||||
always #(DDR3_CLK_PERIOD/2) i_ddr3_clk = !i_ddr3_clk;
|
||||
always #2500 i_ref_clk = !i_ref_clk;
|
||||
initial begin //90 degree phase shifted ddr3_clk
|
||||
#(DDR3_CLK_PERIOD/4);
|
||||
while(1) begin
|
||||
#(DDR3_CLK_PERIOD/2) i_ddr3_clk_90 = !i_ddr3_clk_90;
|
||||
end
|
||||
end
|
||||
initial begin
|
||||
i_controller_clk = 1;
|
||||
i_ddr3_clk = 1;
|
||||
i_ref_clk = 1;
|
||||
i_ddr3_clk_90 = 1;
|
||||
i_rst_n = 0;
|
||||
#1_000_000;
|
||||
i_rst_n = 1;
|
||||
wait(done);
|
||||
#1_000_000;
|
||||
$finish;
|
||||
end
|
||||
|
||||
wire [31 : 0] m_axi_lite_ch1_awaddr;
|
||||
wire [2 : 0] m_axi_lite_ch1_awprot;
|
||||
wire m_axi_lite_ch1_awvalid;
|
||||
wire m_axi_lite_ch1_awready;
|
||||
wire [31 : 0] m_axi_lite_ch1_wdata;
|
||||
wire [3 : 0] m_axi_lite_ch1_wstrb;
|
||||
wire m_axi_lite_ch1_wvalid;
|
||||
wire m_axi_lite_ch1_wready;
|
||||
wire [1 : 0] m_axi_lite_ch1_bresp;
|
||||
wire m_axi_lite_ch1_bvalid;
|
||||
wire m_axi_lite_ch1_bready;
|
||||
wire [31 : 0] m_axi_lite_ch1_araddr;
|
||||
wire m_axi_lite_ch1_arvalid;
|
||||
wire m_axi_lite_ch1_arready;
|
||||
wire [31 : 0] m_axi_lite_ch1_rdata;
|
||||
wire m_axi_lite_ch1_rvalid;
|
||||
wire [1 : 0] m_axi_lite_ch1_rresp;
|
||||
wire m_axi_lite_ch1_rready;
|
||||
|
||||
wire done;
|
||||
wire [31 : 0] status;
|
||||
|
||||
axi_traffic_gen_1 axi_traffic_gen_inst(
|
||||
.s_axi_aclk(i_controller_clk),
|
||||
.s_axi_aresetn(i_rst_n && (dut.ddr3_top_inst.ddr3_controller_inst.state_calibrate == 23)), //stay reset until calibration is done
|
||||
.m_axi_lite_ch1_awaddr(m_axi_lite_ch1_awaddr),
|
||||
.m_axi_lite_ch1_awprot(m_axi_lite_ch1_awprot),
|
||||
.m_axi_lite_ch1_awvalid(m_axi_lite_ch1_awvalid),
|
||||
.m_axi_lite_ch1_awready(m_axi_lite_ch1_awready),
|
||||
.m_axi_lite_ch1_wdata(m_axi_lite_ch1_wdata),
|
||||
.m_axi_lite_ch1_wstrb(m_axi_lite_ch1_wstrb), // assuming full byte enable for simplicity
|
||||
.m_axi_lite_ch1_wvalid(m_axi_lite_ch1_wvalid),
|
||||
.m_axi_lite_ch1_wready(m_axi_lite_ch1_wready),
|
||||
.m_axi_lite_ch1_bresp(m_axi_lite_ch1_bresp),
|
||||
.m_axi_lite_ch1_bvalid(m_axi_lite_ch1_bvalid),
|
||||
.m_axi_lite_ch1_bready(m_axi_lite_ch1_bready),
|
||||
.m_axi_lite_ch1_araddr(m_axi_lite_ch1_araddr),
|
||||
.m_axi_lite_ch1_arvalid(m_axi_lite_ch1_arvalid),
|
||||
.m_axi_lite_ch1_arready(m_axi_lite_ch1_arready),
|
||||
.m_axi_lite_ch1_rdata(m_axi_lite_ch1_rdata),
|
||||
.m_axi_lite_ch1_rvalid(m_axi_lite_ch1_rvalid),
|
||||
.m_axi_lite_ch1_rresp(m_axi_lite_ch1_rresp),
|
||||
.m_axi_lite_ch1_rready(m_axi_lite_ch1_rready),
|
||||
.done(done),
|
||||
.status(status)
|
||||
);
|
||||
|
||||
ddr3_top_axi #(
|
||||
.CONTROLLER_CLK_PERIOD(10_000), //ps, clock period of the controller interface
|
||||
.DDR3_CLK_PERIOD(2_500), //ps, clock period of the DDR3 RAM device (must be 1/4 of the CONTROLLER_CLK_PERIOD)
|
||||
.ROW_BITS(14), //width of row address
|
||||
.COL_BITS(10), //width of column address
|
||||
.BA_BITS(3), //width of bank address
|
||||
.BYTE_LANES(2), //number of byte lanes of DDR3 RAM
|
||||
.AXI_ID_WIDTH(4), // The AXI id width used for R&W, an int between 1-16
|
||||
.MICRON_SIM(1), //enable faster simulation for micron ddr3 model (shorten POWER_ON_RESET_HIGH and INITIAL_CKE_LOW)
|
||||
.ODELAY_SUPPORTED(0), //set to 1 when ODELAYE2 is supported
|
||||
.SECOND_WISHBONE(0) //set to 1 if 2nd wishbone for debugging is needed
|
||||
) dut (
|
||||
.i_controller_clk(i_controller_clk),
|
||||
.i_ddr3_clk(i_ddr3_clk),
|
||||
.i_ref_clk(i_ref_clk), //i_controller_clk = CONTROLLER_CLK_PERIOD, i_ddr3_clk = DDR3_CLK_PERIOD, i_ref_clk = 200MHz
|
||||
.i_ddr3_clk_90(i_ddr3_clk_90), //required only when ODELAY_SUPPORTED is zero
|
||||
.i_rst_n(i_rst_n),
|
||||
|
||||
// AXI Interface
|
||||
// AXI write address channel signals
|
||||
.s_axi_awvalid(m_axi_lite_ch1_awvalid),
|
||||
.s_axi_awready(m_axi_lite_ch1_awready),
|
||||
.s_axi_awid(4'b0000), // AXI-Lite doesn't have ID, so set to 0
|
||||
.s_axi_awaddr(m_axi_lite_ch1_awaddr),
|
||||
.s_axi_awlen(8'b00000000), // AXI-Lite doesn't use burst length
|
||||
.s_axi_awsize(3'b010), // Assuming 32-bit size
|
||||
.s_axi_awburst(2'b01), // AXI-Lite doesn't use burst, set to incrementing
|
||||
.s_axi_awlock(1'b0), // AXI-Lite doesn't use lock, set to 0
|
||||
.s_axi_awcache(4'b0000), // Assuming normal non-cacheable memory
|
||||
.s_axi_awprot(m_axi_lite_ch1_awprot ), // Set to normal, non-secure, data access
|
||||
.s_axi_awqos(4'b0000), // Quality of Service, set to 0
|
||||
|
||||
// AXI write data channel signals
|
||||
.s_axi_wvalid(m_axi_lite_ch1_wvalid),
|
||||
.s_axi_wready(m_axi_lite_ch1_wready),
|
||||
.s_axi_wdata({128'd0,m_axi_lite_ch1_wdata}),
|
||||
.s_axi_wstrb({0,m_axi_lite_ch1_wstrb}), // Assuming full byte enable
|
||||
.s_axi_wlast(m_axi_lite_ch1_wvalid), // AXI-Lite only has single beat transfers
|
||||
|
||||
// AXI write response channel signals
|
||||
.s_axi_bvalid(m_axi_lite_ch1_bvalid),
|
||||
.s_axi_bready(m_axi_lite_ch1_bready),
|
||||
.s_axi_bid(), // AXI-Lite doesn't have ID, so set to 0
|
||||
.s_axi_bresp(m_axi_lite_ch1_bresp),
|
||||
|
||||
// AXI read address channel signals
|
||||
.s_axi_arvalid(m_axi_lite_ch1_arvalid), // No read transactions in this example
|
||||
.s_axi_arready(m_axi_lite_ch1_arready),
|
||||
.s_axi_arid(4'b0000),
|
||||
.s_axi_araddr(m_axi_lite_ch1_araddr),
|
||||
.s_axi_arlen(8'b00000000),
|
||||
.s_axi_arsize(3'b010),
|
||||
.s_axi_arburst(2'b01),
|
||||
.s_axi_arlock(1'b0),
|
||||
.s_axi_arcache(4'b0000),
|
||||
.s_axi_arprot(3'b000),
|
||||
.s_axi_arqos(4'b0000),
|
||||
|
||||
// AXI read data channel signals
|
||||
.s_axi_rvalid(m_axi_lite_ch1_rvalid),
|
||||
.s_axi_rready(m_axi_lite_ch1_rready), // No read transactions in this example
|
||||
.s_axi_rid(),
|
||||
.s_axi_rdata(m_axi_lite_ch1_rdata),
|
||||
.s_axi_rlast(),
|
||||
.s_axi_rresp(m_axi_lite_ch1_rresp),
|
||||
|
||||
// DDR3 I/O Interface
|
||||
.o_ddr3_clk_p(o_ddr3_clk_p),
|
||||
.o_ddr3_clk_n(o_ddr3_clk_n),
|
||||
.o_ddr3_reset_n(o_ddr3_reset_n),
|
||||
.o_ddr3_cke(o_ddr3_cke),
|
||||
.o_ddr3_cs_n(o_ddr3_cs_n),
|
||||
.o_ddr3_ras_n(o_ddr3_ras_n),
|
||||
.o_ddr3_cas_n(o_ddr3_cas_n),
|
||||
.o_ddr3_we_n(o_ddr3_we_n),
|
||||
.o_ddr3_addr(o_ddr3_addr),
|
||||
.o_ddr3_ba_addr(o_ddr3_ba_addr),
|
||||
.io_ddr3_dq(io_ddr3_dq),
|
||||
.io_ddr3_dqs(io_ddr3_dqs),
|
||||
.io_ddr3_dqs_n(io_ddr3_dqs_n),
|
||||
.o_ddr3_dm(o_ddr3_dm),
|
||||
.o_ddr3_odt(o_ddr3_odt)
|
||||
);
|
||||
|
||||
|
||||
ddr3 ddr3_0(
|
||||
.rst_n(o_ddr3_reset_n),
|
||||
.ck(o_ddr3_clk_p),
|
||||
.ck_n(o_ddr3_clk_n),
|
||||
.cke(o_ddr3_cke),
|
||||
.cs_n(o_ddr3_cs_n),
|
||||
.ras_n(o_ddr3_ras_n),
|
||||
.cas_n(o_ddr3_cas_n),
|
||||
.we_n(o_ddr3_we_n),
|
||||
.dm_tdqs(o_ddr3_dm),
|
||||
.ba(o_ddr3_ba_addr),
|
||||
.addr({0,o_ddr3_addr}),
|
||||
.dq(io_ddr3_dq),
|
||||
.dqs(io_ddr3_dqs),
|
||||
.dqs_n(io_ddr3_dqs_n),
|
||||
.tdqs_n(),
|
||||
.odt(o_ddr3_odt)
|
||||
);
|
||||
|
||||
|
||||
|
||||
endmodule
|
||||
Loading…
Reference in New Issue