From 66f0daf0e93cd3ee6246f9def14570cebb9154ac Mon Sep 17 00:00:00 2001 From: AngeloJacobo Date: Sat, 1 Jun 2024 15:30:15 +0800 Subject: [PATCH] added AXI4 feature --- rtl/axi_addr.v | 235 ++++++++++ rtl/axim2wbsp.v | 317 +++++++++++++ rtl/aximrd2wbsp.v | 740 +++++++++++++++++++++++++++++ rtl/aximwr2wbsp.v | 751 ++++++++++++++++++++++++++++++ rtl/ddr3_top_axi.v | 258 ++++++++++ rtl/migsdram.v | 313 +++++++++++++ rtl/sfifo.v | 482 +++++++++++++++++++ rtl/skidbuffer.v | 495 ++++++++++++++++++++ rtl/wbarbiter.v | 404 ++++++++++++++++ testbench/ddr3_axi.sv | 282 +++++++++++ testbench/ddr3_axi_traffic_gen.sv | 202 ++++++++ 11 files changed, 4479 insertions(+) create mode 100644 rtl/axi_addr.v create mode 100644 rtl/axim2wbsp.v create mode 100644 rtl/aximrd2wbsp.v create mode 100644 rtl/aximwr2wbsp.v create mode 100644 rtl/ddr3_top_axi.v create mode 100644 rtl/migsdram.v create mode 100644 rtl/sfifo.v create mode 100644 rtl/skidbuffer.v create mode 100644 rtl/wbarbiter.v create mode 100644 testbench/ddr3_axi.sv create mode 100644 testbench/ddr3_axi_traffic_gen.sv diff --git a/rtl/axi_addr.v b/rtl/axi_addr.v new file mode 100644 index 0000000..8d8ac75 --- /dev/null +++ b/rtl/axi_addr.v @@ -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<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 diff --git a/rtl/axim2wbsp.v b/rtl/axim2wbsp.v new file mode 100644 index 0000000..fb58458 --- /dev/null +++ b/rtl/axim2wbsp.v @@ -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 diff --git a/rtl/aximrd2wbsp.v b/rtl/aximrd2wbsp.v new file mode 100644 index 0000000..07dd7a7 --- /dev/null +++ b/rtl/aximrd2wbsp.v @@ -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= (1<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<= (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 diff --git a/rtl/aximwr2wbsp.v b/rtl/aximwr2wbsp.v new file mode 100644 index 0000000..074467c --- /dev/null +++ b/rtl/aximwr2wbsp.v @@ -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 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 diff --git a/rtl/ddr3_top_axi.v b/rtl/ddr3_top_axi.v new file mode 100644 index 0000000..7b36347 --- /dev/null +++ b/rtl/ddr3_top_axi.v @@ -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 + \ No newline at end of file diff --git a/rtl/migsdram.v b/rtl/migsdram.v new file mode 100644 index 0000000..c1671c8 --- /dev/null +++ b/rtl/migsdram.v @@ -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 diff --git a/rtl/sfifo.v b/rtl/sfifo.v new file mode 100644 index 0000000..c60dffe --- /dev/null +++ b/rtl/sfifo.v @@ -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<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 diff --git a/rtl/skidbuffer.v b/rtl/skidbuffer.v new file mode 100644 index 0000000..3d19cbb --- /dev/null +++ b/rtl/skidbuffer.v @@ -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 diff --git a/rtl/wbarbiter.v b/rtl/wbarbiter.v new file mode 100644 index 0000000..e068278 --- /dev/null +++ b/rtl/wbarbiter.v @@ -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 diff --git a/testbench/ddr3_axi.sv b/testbench/ddr3_axi.sv new file mode 100644 index 0000000..7129c00 --- /dev/null +++ b/testbench/ddr3_axi.sv @@ -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 \ No newline at end of file diff --git a/testbench/ddr3_axi_traffic_gen.sv b/testbench/ddr3_axi_traffic_gen.sv new file mode 100644 index 0000000..ac568db --- /dev/null +++ b/testbench/ddr3_axi_traffic_gen.sv @@ -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 \ No newline at end of file