diff options
Diffstat (limited to 'rtl/wb2axip/axil2apb.v')
| -rw-r--r-- | rtl/wb2axip/axil2apb.v | 717 |
1 files changed, 717 insertions, 0 deletions
diff --git a/rtl/wb2axip/axil2apb.v b/rtl/wb2axip/axil2apb.v new file mode 100644 index 0000000..d8a73ae --- /dev/null +++ b/rtl/wb2axip/axil2apb.v @@ -0,0 +1,717 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Filename: axil2apb.v +// {{{ +// Project: WB2AXIPSP: bus bridges and other odds and ends +// +// Purpose: High throughput AXI-lite bridge to APB. With both skid +// buffers enabled, it can handle 50% throughput--the maximum +// that APB can handle. +// +// Creator: Dan Gisselquist, Ph.D. +// Gisselquist Technology, LLC +// +//////////////////////////////////////////////////////////////////////////////// +// }}} +// Copyright (C) 2020-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 axil2apb #( + // {{{ + parameter C_AXI_ADDR_WIDTH = 32, + parameter C_AXI_DATA_WIDTH = 32, + // OPT_OUTGOING_SKIDBUFFER: required for 50% throughput + parameter [0:0] OPT_OUTGOING_SKIDBUFFER = 1'b0 + // }}} + ) ( + // {{{ + input wire S_AXI_ACLK, + input wire S_AXI_ARESETN, + // + // The AXI-lite interface + // {{{ + input wire S_AXI_AWVALID, + output wire S_AXI_AWREADY, + input wire [C_AXI_ADDR_WIDTH-1:0] S_AXI_AWADDR, + input wire [2:0] S_AXI_AWPROT, + // + 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, + // + output reg S_AXI_BVALID, + input wire S_AXI_BREADY, + output reg [1:0] S_AXI_BRESP, + // + input wire S_AXI_ARVALID, + output wire S_AXI_ARREADY, + input wire [C_AXI_ADDR_WIDTH-1:0] S_AXI_ARADDR, + input wire [2:0] S_AXI_ARPROT, + // + output reg S_AXI_RVALID, + input wire S_AXI_RREADY, + output reg [C_AXI_DATA_WIDTH-1:0] S_AXI_RDATA, + output reg [1:0] S_AXI_RRESP, + // }}} + // + // The APB interface + // {{{ + output reg M_APB_PSEL, + output reg M_APB_PENABLE, + input wire M_APB_PREADY, + output reg [C_AXI_ADDR_WIDTH-1:0] M_APB_PADDR, + output reg M_APB_PWRITE, + output reg [C_AXI_DATA_WIDTH-1:0] M_APB_PWDATA, + output reg [C_AXI_DATA_WIDTH/8-1:0] M_APB_PWSTRB, + output reg [2:0] M_APB_PPROT, + input wire [C_AXI_DATA_WIDTH-1:0] M_APB_PRDATA, + input wire M_APB_PSLVERR + // }}} + // }}} + ); + + // Register declarations + // {{{ + localparam AW = C_AXI_ADDR_WIDTH; + localparam DW = C_AXI_DATA_WIDTH; + localparam AXILLSB = $clog2(C_AXI_DATA_WIDTH)-3; + wire awskd_valid, wskd_valid, arskd_valid; + reg axil_write_ready, axil_read_ready, + write_grant, apb_idle; + wire [AW-AXILLSB-1:0] awskd_addr, arskd_addr; + wire [DW-1:0] wskd_data; + wire [DW/8-1:0] wskd_strb; + wire [2:0] awskd_prot, arskd_prot; + reg apb_bvalid, apb_rvalid, apb_error, out_skid_full; + reg [DW-1:0] apb_data; + // }}} + //////////////////////////////////////////////////////////////////////// + // + // Incoming AXI-lite write interface + // {{{ + //////////////////////////////////////////////////////////////////////// + // + // + + // awskd - write address skid buffer + // {{{ + skidbuffer #(.DW(C_AXI_ADDR_WIDTH-AXILLSB + 3), + .OPT_OUTREG(0) + ) awskd (.i_clk(S_AXI_ACLK), .i_reset(!S_AXI_ARESETN), + .i_valid(S_AXI_AWVALID), .o_ready(S_AXI_AWREADY), + .i_data({ S_AXI_AWADDR[AW-1:AXILLSB], S_AXI_AWPROT }), + .o_valid(awskd_valid), .i_ready(axil_write_ready), + .o_data({ awskd_addr, awskd_prot })); + // }}} + + // wskd - write data skid buffer + // {{{ + skidbuffer #(.DW(DW+(DW/8)), + .OPT_OUTREG(0) + ) wskd (.i_clk(S_AXI_ACLK), .i_reset(!S_AXI_ARESETN), + .i_valid(S_AXI_WVALID), .o_ready(S_AXI_WREADY), + .i_data({ S_AXI_WDATA, S_AXI_WSTRB }), + .o_valid(wskd_valid), .i_ready(axil_write_ready), + .o_data({ wskd_data, wskd_strb })); + // }}} + + // apb_idle + // {{{ + always @(*) + begin + apb_idle = !M_APB_PSEL;// || (M_APB_PENABLE && M_APB_PREADY); + if (OPT_OUTGOING_SKIDBUFFER && (M_APB_PENABLE && M_APB_PREADY)) + apb_idle = 1'b1; + end + // }}} + + // axil_write_ready + // {{{ + always @(*) + begin + axil_write_ready = apb_idle; + if (S_AXI_BVALID && !S_AXI_BREADY) + axil_write_ready = 1'b0; + if (!awskd_valid || !wskd_valid) + axil_write_ready = 1'b0; + if (!write_grant && arskd_valid) + axil_write_ready = 1'b0; + end + // }}} + // }}} + //////////////////////////////////////////////////////////////////////// + // + // Incoming AXI-lite read interface + // {{{ + //////////////////////////////////////////////////////////////////////// + // + // + + // arskd buffer + // {{{ + skidbuffer #(.DW(C_AXI_ADDR_WIDTH-AXILLSB+3), + .OPT_OUTREG(0) + ) arskd (.i_clk(S_AXI_ACLK), .i_reset(!S_AXI_ARESETN), + .i_valid(S_AXI_ARVALID), .o_ready(S_AXI_ARREADY), + .i_data({ S_AXI_ARADDR[AW-1:AXILLSB], S_AXI_ARPROT }), + .o_valid(arskd_valid), .i_ready(axil_read_ready), + .o_data({ arskd_addr, arskd_prot })); + // }}} + + // axil_read_ready + // {{{ + always @(*) + begin + axil_read_ready = apb_idle; + if (S_AXI_RVALID && !S_AXI_RREADY) + axil_read_ready = 1'b0; + if (write_grant && awskd_valid && wskd_valid) + axil_read_ready = 1'b0; + if (!arskd_valid) + axil_read_ready = 1'b0; + end + // }}} + // }}} + //////////////////////////////////////////////////////////////////////// + // + // Arbitrate among reads and writes --- alternating arbitration + // {{{ + //////////////////////////////////////////////////////////////////////// + // + // + + // write_grant -- alternates + // {{{ + always @(posedge S_AXI_ACLK) + if (apb_idle) + begin + if (axil_write_ready) + write_grant <= 1'b0; + else if (axil_read_ready) + write_grant <= 1'b1; + end + // }}} + + // }}} + //////////////////////////////////////////////////////////////////////// + // + // Drive the APB bus + // {{{ + //////////////////////////////////////////////////////////////////////// + // + // + + // APB bus + // {{{ + initial M_APB_PSEL = 1'b0; + initial M_APB_PENABLE = 1'b0; + always @(posedge S_AXI_ACLK) + begin + if (apb_idle) + begin + M_APB_PSEL <= 1'b0; + if (axil_read_ready) + begin + M_APB_PSEL <= 1'b1; + M_APB_PADDR <= { arskd_addr, {(AXILLSB){1'b0}} }; + M_APB_PWRITE <= 1'b0; + M_APB_PPROT <= arskd_prot; + end else if (axil_write_ready) + begin + M_APB_PSEL <= 1'b1; + M_APB_PADDR <= { awskd_addr, {(AXILLSB){1'b0}} }; + M_APB_PWRITE <= 1'b1; + M_APB_PPROT <= awskd_prot; + end + + if (wskd_valid) + begin + M_APB_PWDATA <= wskd_data; + M_APB_PWSTRB <= wskd_strb; + end + + M_APB_PENABLE <= 1'b0; + end else if (!M_APB_PENABLE) + M_APB_PENABLE <= 1'b1; + else if (M_APB_PREADY) + begin // if (M_APB_PSEL && M_APB_ENABLE) + M_APB_PENABLE <= 1'b0; + M_APB_PSEL <= 1'b0; + end + + if (!S_AXI_ARESETN) + begin + M_APB_PSEL <= 1'b0; + M_APB_PENABLE <= 1'b0; + end + end + // }}} + + reg r_apb_bvalid, r_apb_rvalid, r_apb_error; + reg [DW-1:0] r_apb_data; + + generate if (OPT_OUTGOING_SKIDBUFFER) + begin : GEN_OSKID + // {{{ + // r_apb_bvalid, r_apb_rvalid, r_apb_error, r_apb_data + // {{{ + initial r_apb_bvalid = 1'b0; + initial r_apb_rvalid = 1'b0; + always @(posedge S_AXI_ACLK) + begin + if (M_APB_PSEL && M_APB_PENABLE && M_APB_PREADY) + begin + r_apb_bvalid <= (S_AXI_BVALID && !S_AXI_BREADY) && M_APB_PWRITE; + r_apb_rvalid <= (S_AXI_RVALID && !S_AXI_RREADY) && !M_APB_PWRITE; + if (!M_APB_PWRITE) + r_apb_data <= M_APB_PRDATA; + r_apb_error <= M_APB_PSLVERR; + end else begin + if (S_AXI_BREADY) + r_apb_bvalid <= 1'b0; + if (S_AXI_RREADY) + r_apb_rvalid <= 1'b0; + end + + if (!S_AXI_ARESETN) + begin + r_apb_bvalid <= 1'b0; + r_apb_rvalid <= 1'b0; + end + end + // }}} + + // apb_bvalid + // {{{ + always @(*) + apb_bvalid = (M_APB_PSEL && M_APB_PENABLE + && M_APB_PREADY && M_APB_PWRITE)|| r_apb_bvalid; + // }}} + + // apb_rvalid + // {{{ + always @(*) + apb_rvalid = (M_APB_PSEL && M_APB_PENABLE + && M_APB_PREADY && !M_APB_PWRITE)||r_apb_rvalid; + // }}} + + // apb_data + // {{{ + always @(*) + if (out_skid_full) + apb_data = r_apb_data; + else + apb_data = M_APB_PRDATA; + // }}} + + // apb_error + // {{{ + always @(*) + if (out_skid_full) + apb_error = r_apb_error; + else + apb_error = M_APB_PSLVERR; + // }}} + + always @(*) + out_skid_full = r_apb_bvalid || r_apb_rvalid; + // }}} + end else begin : NO_OSKID + // {{{ + + initial r_apb_bvalid = 1'b0; + initial r_apb_rvalid = 1'b0; + initial r_apb_error = 1'b0; + initial r_apb_data = 0; + always @(*) + begin + r_apb_bvalid = 1'b0; + r_apb_rvalid = 1'b0; + r_apb_error = 1'b0; + r_apb_data = 0; + + apb_bvalid = M_APB_PSEL && M_APB_PENABLE + && M_APB_PREADY && M_APB_PWRITE; + + apb_rvalid = M_APB_PSEL && M_APB_PENABLE + && M_APB_PREADY && !M_APB_PWRITE; + + apb_data = M_APB_PRDATA; + + apb_error = M_APB_PSLVERR; + + out_skid_full = 1'b0; + end + + // Verilator lint_off UNUSED + wire skd_unused; + assign skd_unused = &{ 1'b0, r_apb_bvalid, r_apb_rvalid, + r_apb_data, r_apb_error, out_skid_full }; + // Verilator lint_on UNUSED + // }}} + end endgenerate + + + // }}} + //////////////////////////////////////////////////////////////////////// + // + // AXI-lite write return signaling + // {{{ + //////////////////////////////////////////////////////////////////////// + // + // + + // BVALID + // {{{ + initial S_AXI_BVALID = 1'b0; + always @(posedge S_AXI_ACLK) + if (!S_AXI_ARESETN) + S_AXI_BVALID <= 1'b0; + else if (!S_AXI_BVALID || S_AXI_BREADY) + S_AXI_BVALID <= apb_bvalid; + // }}} + + // BRESP + // {{{ + initial S_AXI_BRESP = 2'b00; + always @(posedge S_AXI_ACLK) + if (!S_AXI_ARESETN) + S_AXI_BRESP <= 2'b00; + else if (!S_AXI_BVALID || S_AXI_BREADY) + S_AXI_BRESP <= { apb_error, 1'b0 }; + // }}} + + // }}} + //////////////////////////////////////////////////////////////////////// + // + // AXI-lite read return signaling + // {{{ + //////////////////////////////////////////////////////////////////////// + // + // + + + // RVALID + // {{{ + initial S_AXI_RVALID = 1'b0; + always @(posedge S_AXI_ACLK) + if (!S_AXI_ARESETN) + S_AXI_RVALID <= 1'b0; + else if (!S_AXI_RVALID || S_AXI_RREADY) + S_AXI_RVALID <= apb_rvalid; + // }}} + + // RRESP + // {{{ + initial S_AXI_RRESP = 2'b00; + always @(posedge S_AXI_ACLK) + if (!S_AXI_ARESETN) + S_AXI_RRESP <= 2'b00; + else if (!S_AXI_RVALID || S_AXI_RREADY) + S_AXI_RRESP <= { apb_error, 1'b0 }; + // }}} + + // RDATA + // {{{ + always @(posedge S_AXI_ACLK) + if ((!S_AXI_RVALID || S_AXI_RREADY) && apb_rvalid) + S_AXI_RDATA <= apb_data; + // }}} + + // }}} + + // Make Verilator happy + // {{{ + // Verilator lint_off UNUSED + wire unused; + assign unused = &{ 1'b0, S_AXI_AWPROT, S_AXI_ARPROT, + S_AXI_AWADDR[AXILLSB-1:0], S_AXI_ARADDR[AXILLSB-1:0] + }; + // Verilator lint_on UNUSED + // }}} +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +// +// Formal properties +// {{{ +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +`ifdef FORMAL + localparam F_LGDEPTH = 3; + + wire [F_LGDEPTH-1:0] faxi_rd_outstanding, + faxi_wr_outstanding, + faxi_awr_outstanding; + reg f_past_valid; + + initial f_past_valid = 1'b0; + always @(posedge S_AXI_ACLK) + f_past_valid <= 1'b1; + + //////////////////////////////////////////////////////////////////////// + // + // AXI-lite interface properties + // {{{ + //////////////////////////////////////////////////////////////////////// + // + // + + faxil_slave #( + // {{{ + .C_AXI_ADDR_WIDTH(C_AXI_ADDR_WIDTH), + .C_AXI_DATA_WIDTH (C_AXI_DATA_WIDTH), + .F_OPT_COVER_BURST(4), + .F_AXI_MAXWAIT(17), + .F_AXI_MAXDELAY(17), + .F_AXI_MAXRSTALL(3), + .F_LGDEPTH(F_LGDEPTH) + // }}} + ) faxil ( + // {{{ + .i_clk(S_AXI_ACLK), // System clock + .i_axi_reset_n(S_AXI_ARESETN), + // + .i_axi_awvalid(S_AXI_AWVALID), + .i_axi_awready(S_AXI_AWREADY), + .i_axi_awaddr(S_AXI_AWADDR), + .i_axi_awprot(S_AXI_AWPROT), + // + .i_axi_wready(S_AXI_WREADY), + .i_axi_wdata(S_AXI_WDATA), + .i_axi_wstrb(S_AXI_WSTRB), + .i_axi_wvalid(S_AXI_WVALID), + // + .i_axi_bresp(S_AXI_BRESP), + .i_axi_bvalid(S_AXI_BVALID), + .i_axi_bready(S_AXI_BREADY), + // + .i_axi_arvalid(S_AXI_ARVALID), + .i_axi_arready(S_AXI_ARREADY), + .i_axi_araddr(S_AXI_ARADDR), + .i_axi_arprot(S_AXI_ARPROT), + // + .i_axi_rvalid(S_AXI_RVALID), + .i_axi_rready(S_AXI_RREADY), + .i_axi_rresp(S_AXI_RRESP), + .i_axi_rdata(S_AXI_RDATA), + // + .f_axi_rd_outstanding(faxi_rd_outstanding), + .f_axi_wr_outstanding(faxi_wr_outstanding), + .f_axi_awr_outstanding(faxi_awr_outstanding) + // }}} + ); + + // Correlate outstanding counters against our state + // {{{ + always @(*) + if (S_AXI_ARESETN) + begin + assert(faxi_awr_outstanding == (S_AXI_AWREADY ? 0:1) + + (r_apb_bvalid ? 1:0) + + (S_AXI_BVALID ? 1:0) + + ((M_APB_PSEL && M_APB_PWRITE) ? 1:0)); + + assert(faxi_wr_outstanding == (S_AXI_WREADY ? 0:1) + + (r_apb_bvalid ? 1:0) + + (S_AXI_BVALID ? 1:0) + + ((M_APB_PSEL && M_APB_PWRITE) ? 1:0)); + + assert(faxi_rd_outstanding == (S_AXI_ARREADY ? 0:1) + + (r_apb_rvalid ? 1:0) + + (S_AXI_RVALID ? 1:0) + + ((M_APB_PSEL && !M_APB_PWRITE) ? 1:0)); + + if (r_apb_bvalid) + assert(S_AXI_BVALID); + if (r_apb_rvalid) + assert(S_AXI_RVALID); + end + // }}} + + // }}} + //////////////////////////////////////////////////////////////////////// + // + // APB interface properties + // {{{ + //////////////////////////////////////////////////////////////////////// + // + // + + fapb_master #( + // {{{ + .AW(C_AXI_ADDR_WIDTH), + .DW(C_AXI_DATA_WIDTH), + .F_OPT_MAXSTALL(3) + // }}} + ) fapb ( + // {{{ + .PCLK(S_AXI_ACLK), .PRESETn(S_AXI_ARESETN), + .PSEL( M_APB_PSEL), + .PENABLE(M_APB_PENABLE), + .PREADY( M_APB_PREADY), + .PADDR( M_APB_PADDR), + .PWRITE( M_APB_PWRITE), + .PWDATA( M_APB_PWDATA), + .PWSTRB( M_APB_PWSTRB), + .PPROT( M_APB_PPROT), + .PRDATA( M_APB_PRDATA), + .PSLVERR(M_APB_PSLVERR) + // }}} + ); + + always @(*) + if (!M_APB_PSEL) + assert(!M_APB_PENABLE); + + // }}} + //////////////////////////////////////////////////////////////////////// + // + // Induction invariants + // {{{ + //////////////////////////////////////////////////////////////////////// + // + // + + always @(*) + assert(!axil_write_ready || !axil_read_ready); + + // }}} + //////////////////////////////////////////////////////////////////////// + // + // Contract properties + // {{{ + //////////////////////////////////////////////////////////////////////// + // + // + + (* anyconst *) reg f_never_test; + (* anyconst *) reg [AW-1:0] f_never_addr; + (* anyconst *) reg [DW-1:0] f_never_data; + (* anyconst *) reg [DW/8-1:0] f_never_strb; + (* anyconst *) reg [2:0] f_never_prot; + + // Assume the never values are never received + // {{{ + always @(*) + if (f_never_test) + begin + assume(f_never_addr[AXILLSB-1:0] == 0); + + if (S_AXI_AWVALID) + begin + assume(S_AXI_AWADDR[AW-1:AXILLSB] != f_never_addr[AW-1:AXILLSB]); + assume(S_AXI_AWPROT != f_never_prot); + end + + if (S_AXI_WVALID) + begin + assume(S_AXI_WDATA != f_never_data); + assume(S_AXI_WSTRB != f_never_strb); + end + + if (S_AXI_ARVALID) + begin + assume(S_AXI_ARADDR[AW-1:AXILLSB] != f_never_addr[AW-1:AXILLSB]); + assume(S_AXI_ARPROT != f_never_prot); + end + + if (M_APB_PSEL && M_APB_PENABLE && M_APB_PREADY&& !M_APB_PWRITE) + assume(M_APB_PRDATA != f_never_data); + end + // }}} + + // Assert the never values are never in the incoming skid buffers + // {{{ + always @(*) + if (f_never_test) + begin + if (awskd_valid) + begin + assert(awskd_addr != f_never_addr[AW-1:AXILLSB]); + assert(awskd_prot != f_never_prot); + end + + if (wskd_valid) + begin + assert(wskd_data != f_never_data); + assert(wskd_strb != f_never_strb); + end + + if (arskd_valid) + begin + assert(arskd_addr != f_never_addr[AW-1:AXILLSB]); + assert(arskd_prot != f_never_prot); + end + + if (r_apb_rvalid) + assert(r_apb_data != f_never_data); + end + // }}} + + // Assert the never values are never output + // {{{ + always @(*) + if (f_never_test) + begin + if (M_APB_PSEL) + begin + assert(M_APB_PADDR != f_never_addr); + assert(M_APB_PPROT != f_never_prot); + if (M_APB_PWRITE) + begin + assert(M_APB_PWDATA != f_never_data); + assert(M_APB_PWSTRB != f_never_strb); + end + end + + if (S_AXI_RVALID) + assert(S_AXI_RDATA != f_never_data); + end + // }}} + + // }}} + //////////////////////////////////////////////////////////////////////// + // + // Cover checks + // {{{ + //////////////////////////////////////////////////////////////////////// + // + // + + // None (yet) + + // }}} + //////////////////////////////////////////////////////////////////////// + // + // Careless assumptions + // {{{ + //////////////////////////////////////////////////////////////////////// + // + // + + // }}} +`endif +// }}} +endmodule |
