diff --git a/Libraries/AMBA_Fabrics/AXI4/AXI4_Addr_Translator.bsv b/Libraries/AMBA_Fabrics/AXI4/AXI4_Addr_Translator.bsv index 241024d..8513fbd 100644 --- a/Libraries/AMBA_Fabrics/AXI4/AXI4_Addr_Translator.bsv +++ b/Libraries/AMBA_Fabrics/AXI4/AXI4_Addr_Translator.bsv @@ -1,4 +1,5 @@ // Copyright (c) 2020 Bluespec, Inc. All Rights Reserved +// Copyright (c) 2024 Rishiyur S. Nikhil. // // SPDX-License-Identifier: BSD-3-Clause @@ -8,6 +9,8 @@ package AXI4_Addr_Translator; // This package defines transformers for AXI4_M and AXI4_S interfaces // that perform a simple 'address-translator' (add/subtract a fixed // constant from address). +// Each transformer copies AW and AR, just adjusting the address, +// and just copies W, B and R. // ================================================================ // Bluespec library imports @@ -15,169 +18,139 @@ package AXI4_Addr_Translator; // none // ---------------- -// BSV additional libs +// Bluespec misc. libs -// none +import Semi_FIFOF :: *; // ================================================================ // Project imports -import AXI4_Types :: *; +import AXI4_Types :: *; // ================================================================ -// M-to-M interface transformer with address translation +// M-to-M interface transformer with address translation. -function AXI4_M_IFC #(wd_id, wd_addr, wd_data, wd_user) - fv_AXI4_M_Address_Translator (Bool add_not_sub, - Bit #(wd_addr) addr_delta, - AXI4_M_IFC #(wd_id, wd_addr, wd_data, wd_user) ifc); +function AXI4_M_IFC #(wd_id, wd_addr, wd_data, wd_user) + fv_AXI4_M_Address_Translator (Bool add_not_sub, + Bit #(wd_addr) addr_delta, + AXI4_M_IFC #(wd_id, + wd_addr, + wd_data, + wd_user) ifc_M); function Bit #(wd_addr) fv_addr_translate (Bit #(wd_addr) addr); return (add_not_sub ? addr + addr_delta : addr - addr_delta); endfunction return interface AXI4_M_IFC - // Wr Addr channel - method Bool m_awvalid = ifc.m_awvalid; // out - method Bit #(wd_id) m_awid = ifc.m_awid; // out - method Bit #(wd_addr) m_awaddr = fv_addr_translate (ifc.m_awaddr); // out - method Bit #(8) m_awlen = ifc.m_awlen; // out - method AXI4_Size m_awsize = ifc.m_awsize; // out - method Bit #(2) m_awburst = ifc.m_awburst; // out - method Bit #(1) m_awlock = ifc.m_awlock; // out - method Bit #(4) m_awcache = ifc.m_awcache; // out - method Bit #(3) m_awprot = ifc.m_awprot; // out - method Bit #(4) m_awqos = ifc.m_awqos; // out - method Bit #(4) m_awregion = ifc.m_awregion; // out - method Bit #(wd_user) m_awuser = ifc.m_awuser; // out - method Action m_awready (Bool awready) = ifc.m_awready (awready); // in - - // Wr Data channel - method Bool m_wvalid = ifc.m_wvalid; // out - method Bit #(wd_data) m_wdata = ifc.m_wdata; // out - method Bit #(TDiv #(wd_data, 8)) m_wstrb = ifc.m_wstrb; // out - method Bool m_wlast = ifc.m_wlast; // out - method Bit #(wd_user) m_wuser = ifc.m_wuser; // out - - method Action m_wready (Bool wready) = ifc.m_wready (wready); // in - - // Wr Response channel - method Action m_bvalid (Bool bvalid, // in - Bit #(wd_id) bid, // in - Bit #(2) bresp, // in - Bit #(wd_user) buser); // in - ifc.m_bvalid (bvalid, bid, bresp, buser); - endmethod - method Bool m_bready = ifc.m_bready; // out - - // Rd Addr channel - method Bool m_arvalid = ifc.m_arvalid; // out - method Bit #(wd_id) m_arid = ifc.m_arid; // out - method Bit #(wd_addr) m_araddr = fv_addr_translate (ifc.m_araddr); // out - method Bit #(8) m_arlen = ifc.m_arlen; // out - method AXI4_Size m_arsize = ifc.m_arsize; // out - method Bit #(2) m_arburst = ifc.m_arburst; // out - method Bit #(1) m_arlock = ifc.m_arlock; // out - method Bit #(4) m_arcache = ifc.m_arcache; // out - method Bit #(3) m_arprot = ifc.m_arprot; // out - method Bit #(4) m_arqos = ifc.m_arqos; // out - method Bit #(4) m_arregion = ifc.m_arregion; // out - method Bit #(wd_user) m_aruser = ifc.m_aruser; // out - method Action m_arready (Bool arready) = ifc.m_arready (arready); // in - - // Rd Data channel - method Action m_rvalid (Bool rvalid, // in - Bit #(wd_id) rid, // in - Bit #(wd_data) rdata, // in - Bit #(2) rresp, // in - Bool rlast, // in - Bit #(wd_user) ruser); // in - ifc.m_rvalid (rvalid, rid, rdata, rresp, rlast, ruser); - endmethod - method Bool m_rready = ifc.m_rready; // out + interface FIFOF_O o_AW; + method first; + let aw_in = ifc_M.o_AW.first; + let aw_out = AXI4_AW {awid: aw_in.awid, + awaddr: fv_addr_translate (aw_in.awaddr), + awlen: aw_in.awlen, + awsize: aw_in.awsize, + awburst: aw_in.awburst, + awlock: aw_in.awlock, + awcache: aw_in.awcache, + awprot: aw_in.awprot, + awqos: aw_in.awqos, + awregion: aw_in.awregion, + awuser: aw_in.awuser}; + return aw_out; + endmethod + method deq = ifc_M.o_AW.deq; + method notEmpty = ifc_M.o_AW.notEmpty; + endinterface + + interface FIFOF_I o_W = ifc_M.o_W; + + interface FIFOF_I i_B = ifc_M.i_B; + + interface FIFOF_O o_AR; + method first; + let ar_in = ifc_M.o_AR.first; + let ar_out = AXI4_AR {arid: ar_in.arid, + araddr: fv_addr_translate (ar_in.araddr), + arlen: ar_in.arlen, + arsize: ar_in.arsize, + arburst: ar_in.arburst, + arlock: ar_in.arlock, + arcache: ar_in.arcache, + arprot: ar_in.arprot, + arqos: ar_in.arqos, + arregion: ar_in.arregion, + aruser: ar_in.aruser}; + return ar_out; + endmethod + method deq = ifc_M.o_AR.deq; + method notEmpty = ifc_M.o_AR.notEmpty; + endinterface + + interface FIFOF_I i_R = ifc_M.i_R; endinterface; endfunction // ================================================================ // S-to-S interface transformer with address translation -function AXI4_S_IFC #(wd_id, wd_addr, wd_data, wd_user) - fv_AXI4_S_Address_Translator (Bool add_not_sub, - Bit #(wd_addr) addr_delta, - AXI4_S_IFC #(wd_id, wd_addr, wd_data, wd_user) ifc); +function AXI4_S_IFC #(wd_id, wd_addr, wd_data, wd_user) + fv_AXI4_S_Address_Translator (Bool add_not_sub, + Bit #(wd_addr) addr_delta, + AXI4_S_IFC #(wd_id, + wd_addr, + wd_data, + wd_user) ifc_S); function Bit #(wd_addr) fv_addr_translate (Bit #(wd_addr) addr); return (add_not_sub ? addr + addr_delta : addr - addr_delta); endfunction return interface AXI4_S_IFC - // Wr Addr channel - method Action m_awvalid (Bool awvalid, - Bit #(wd_id) awid, - Bit #(wd_addr) awaddr, - Bit #(8) awlen, - AXI4_Size awsize, - Bit #(2) awburst, - Bit #(1) awlock, - Bit #(4) awcache, - Bit #(3) awprot, - Bit #(4) awqos, - Bit #(4) awregion, - Bit #(wd_user) awuser); - ifc.m_awvalid (awvalid, awid, - fv_addr_translate (awaddr), - awlen, awsize, awburst, - awlock, awcache, awprot, awqos, awregion, awuser); - endmethod - method Bool m_awready = ifc.m_awready; - - // Wr Data channel - method Action m_wvalid (Bool wvalid, - Bit #(wd_data) wdata, - Bit #(TDiv #(wd_data, 8)) wstrb, - Bool wlast, - Bit #(wd_user) wuser); - ifc.m_wvalid (wvalid, wdata, wstrb, wlast, wuser); - endmethod - method Bool m_wready = ifc.m_wready; - - // Wr Response channel - method Bool m_bvalid = ifc.m_bvalid; - method Bit #(wd_id) m_bid = ifc.m_bid; - method Bit #(2) m_bresp = ifc.m_bresp; - method Bit #(wd_user) m_buser = ifc.m_buser; - method Action m_bready (Bool bready) = ifc.m_bready (bready); - - // Rd Addr channel - method Action m_arvalid (Bool arvalid, - Bit #(wd_id) arid, - Bit #(wd_addr) araddr, - Bit #(8) arlen, - AXI4_Size arsize, - Bit #(2) arburst, - Bit #(1) arlock, - Bit #(4) arcache, - Bit #(3) arprot, - Bit #(4) arqos, - Bit #(4) arregion, - Bit #(wd_user) aruser); - ifc.m_arvalid (arvalid, arid, - fv_addr_translate (araddr), - arlen, arsize, arburst, - arlock, arcache, arprot, arqos, arregion, aruser); - endmethod - method Bool m_arready = ifc.m_arready; - - // Rd Data channel - method Bool m_rvalid = ifc.m_rvalid; - method Bit #(wd_id) m_rid = ifc.m_rid; - method Bit #(wd_data) m_rdata = ifc.m_rdata; - method Bit #(2) m_rresp = ifc.m_rresp; - method Bool m_rlast = ifc.m_rlast; - method Bit #(wd_user) m_ruser = ifc.m_ruser; - method Action m_rready (Bool rready); - ifc.m_rready (rready); - endmethod + interface FIFOF_I i_AW; + method Action enq (AXI4_AW #(wd_id, wd_addr, wd_user) aw_in); + action + let aw_out = AXI4_AW {awid: aw_in.awid, + awaddr: fv_addr_translate (aw_in.awaddr), + awlen: aw_in.awlen, + awsize: aw_in.awsize, + awburst: aw_in.awburst, + awlock: aw_in.awlock, + awcache: aw_in.awcache, + awprot: aw_in.awprot, + awqos: aw_in.awqos, + awregion: aw_in.awregion, + awuser: aw_in.awuser}; + ifc_S.i_AW.enq (aw_out); + endaction + endmethod + method notFull = ifc_S.i_AW.notFull; + endinterface + + interface FIFOF_I i_W = ifc_S.i_W; + interface FIFOF_O o_B = ifc_S.o_B; + + interface FIFOF_I i_AR; + method Action enq (AXI4_AR #(wd_id, wd_addr, wd_user) ar_in); + action + let ar_out = AXI4_AR {arid: ar_in.arid, + araddr: fv_addr_translate (ar_in.araddr), + arlen: ar_in.arlen, + arsize: ar_in.arsize, + arburst: ar_in.arburst, + arlock: ar_in.arlock, + arcache: ar_in.arcache, + arprot: ar_in.arprot, + arqos: ar_in.arqos, + arregion: ar_in.arregion, + aruser: ar_in.aruser}; + ifc_S.i_AR.enq (ar_out); + endaction + endmethod + method notFull = ifc_S.i_AR.notFull; + endinterface + + interface FIFOF_O o_R = ifc_S.o_R; endinterface; endfunction diff --git a/Libraries/AMBA_Fabrics/AXI4/AXI4_BSV_RTL.bsv b/Libraries/AMBA_Fabrics/AXI4/AXI4_BSV_RTL.bsv new file mode 100644 index 0000000..4b890d9 --- /dev/null +++ b/Libraries/AMBA_Fabrics/AXI4/AXI4_BSV_RTL.bsv @@ -0,0 +1,1065 @@ +// Copyright (c) 2019-2023 Bluespec, Inc. All Rights Reserved +// Copyright (c) 2024 Rishiyur S. Nikhil. + +// SPDX-License-Identifier: BSD-3-Clause + +package AXI4_BSV_RTL; + +// **************************************************************** +// **************************************************************** + +// This package provides facilities for AXI4 connections between BSV +// and RTL: + +// BSV: AW,W,B,AR,R are bit-vectors but always viewed as structs, +// whose fields correspond to AXI4 sub-buses. +// Some of those fields are always viewed as enums +// Communication is via FIFOs. + +// RTL: AW,W,B,AR,R are buses, with named sub-buses. +// Communication is via handshake on two more wires per bus: +// ready,valid + +// Transactors ("xactors") are buffers to cross the BSV-RTL boundary +// The BSV side uses standard BSV FIFO interfaces. +// The RTL side uses standard AMBA signaling, allowing it to connect +// to any AXI IP written in RTL or in BSV. +// +// Two kinds of transactors +// (in both cases "x_to_y" means to connect an M to an S +// +// +----------------------------+ +// | AXI4_BSV_to_RTL | +// BSV |AXI4_S_IFC AXI4_RTL_M_IFC| RTL +// M <---> |ifc_S rtl_M|<--> S +// +----------------------------+ +// +// +----------------------------+ +// | AXI4_RTL_to_BSV | +// RTL |AXI4_RTL_S_IFC AXI4_M_IFC| BSV +// M <---> |rtl_S ifc_M|<--> S +// +----------------------------+ +// +// **************************************************************** +// **************************************************************** +// BSV library imports + +import FIFOF :: *; +import Connectable :: *; + +// ---------------- +// Bluespec misc. libs + +import Semi_FIFOF :: *; +import EdgeFIFOFs :: *; + +// ---------------- +// Project imports + +import AXI4_Types :: *; + +// **************************************************************** +// **************************************************************** +// Section: Interfaces with standard AMBA RTL-level signaling +// (ready/valid handshakes, standard AMBA signal names) + +// ================================================================ +// These are the signal-level interfaces for an AXI4 M. +// The (*..*) attributes ensure that when bsc compiles this to Verilog, +// we get exactly the signals specified in the ARM spec. + +interface AXI4_RTL_M_IFC #(numeric type wd_id, + numeric type wd_addr, + numeric type wd_data, + numeric type wd_user); + // ---------------- + // Wr Addr channel + (* always_ready, result="awvalid" *) method Bool m_awvalid; // out + + (* always_ready, result="awid" *) method Bit #(wd_id) m_awid; // out + (* always_ready, result="awaddr" *) method Bit #(wd_addr) m_awaddr; // out + (* always_ready, result="awlen" *) method Bit #(8) m_awlen; // out + (* always_ready, result="awsize" *) method AXI4_Size m_awsize; // out + (* always_ready, result="awburst" *) method Bit #(2) m_awburst; // out + (* always_ready, result="awlock" *) method Bit #(1) m_awlock; // out + (* always_ready, result="awcache" *) method Bit #(4) m_awcache; // out + (* always_ready, result="awprot" *) method Bit #(3) m_awprot; // out + (* always_ready, result="awqos" *) method Bit #(4) m_awqos; // out + (* always_ready, result="awregion" *) method Bit #(4) m_awregion; // out + (* always_ready, result="awuser" *) method Bit #(wd_user) m_awuser; // out + + (* always_ready, always_enabled, prefix="" *) + method Action m_awready ((* port="awready" *) Bool awready); // in + + // ---------------- + // Wr Data channel + (* always_ready, result="wvalid" *) method Bool m_wvalid; // out + + (* always_ready, result="wdata" *) method Bit #(wd_data) m_wdata; // out + (* always_ready, result="wstrb" *) method Bit #(TDiv #(wd_data, 8)) m_wstrb; // out + (* always_ready, result="wlast" *) method Bool m_wlast; // out + (* always_ready, result="wuser" *) method Bit #(wd_user) m_wuser; // out + + (* always_ready, always_enabled, prefix = "" *) + method Action m_wready ((* port="wready" *) Bool wready); // in + + // ---------------- + // Wr Response channel + (* always_ready, always_enabled, prefix = "" *) + method Action m_bvalid ((* port="bvalid" *) Bool bvalid, // in + (* port="bid" *) Bit #(wd_id) bid, // in + (* port="bresp" *) Bit #(2) bresp, // in + (* port="buser" *) Bit #(wd_user) buser); // in + + (* always_ready, prefix = "", result="bready" *) + method Bool m_bready; // out + + // ---------------- + // Rd Addr channel + (* always_ready, result="arvalid" *) method Bool m_arvalid; // out + + (* always_ready, result="arid" *) method Bit #(wd_id) m_arid; // out + (* always_ready, result="araddr" *) method Bit #(wd_addr) m_araddr; // out + (* always_ready, result="arlen" *) method Bit #(8) m_arlen; // out + (* always_ready, result="arsize" *) method AXI4_Size m_arsize; // out + (* always_ready, result="arburst" *) method Bit #(2) m_arburst; // out + (* always_ready, result="arlock" *) method Bit #(1) m_arlock; // out + (* always_ready, result="arcache" *) method Bit #(4) m_arcache; // out + (* always_ready, result="arprot" *) method Bit #(3) m_arprot; // out + (* always_ready, result="arqos" *) method Bit #(4) m_arqos; // out + (* always_ready, result="arregion" *) method Bit #(4) m_arregion; // out + (* always_ready, result="aruser" *) method Bit #(wd_user) m_aruser; // out + + (* always_ready, always_enabled, prefix="" *) + method Action m_arready ((* port="arready" *) Bool arready); // in + + // ---------------- + // Rd Data channel + (* always_ready, always_enabled, prefix = "" *) + method Action m_rvalid ((* port="rvalid" *) Bool rvalid, // in + (* port="rid" *) Bit #(wd_id) rid, // in + (* port="rdata" *) Bit #(wd_data) rdata, // in + (* port="rresp" *) Bit #(2) rresp, // in + (* port="rlast" *) Bool rlast, // in + (* port="ruser" *) Bit #(wd_user) ruser); // in + + (* always_ready, result="rready" *) + method Bool m_rready; // out +endinterface: AXI4_RTL_M_IFC + +// ================================================================ +// These are the signal-level interfaces for an AXI4-Lite S. +// The (*..*) attributes ensure that when bsc compiles this to Verilog, +// we get exactly the signals specified in the ARM spec. + +interface AXI4_RTL_S_IFC #(numeric type wd_id, + numeric type wd_addr, + numeric type wd_data, + numeric type wd_user); + // Wr Addr channel + (* always_ready, always_enabled, prefix = "" *) + method Action m_awvalid ((* port="awvalid" *) Bool awvalid, // in + (* port="awid" *) Bit #(wd_id) awid, // in + (* port="awaddr" *) Bit #(wd_addr) awaddr, // in + (* port="awlen" *) Bit #(8) awlen, // in + (* port="awsize" *) AXI4_Size awsize, // in + (* port="awburst" *) Bit #(2) awburst, // in + (* port="awlock" *) Bit #(1) awlock, // in + (* port="awcache" *) Bit #(4) awcache, // in + (* port="awprot" *) Bit #(3) awprot, // in + (* port="awqos" *) Bit #(4) awqos, // in + (* port="awregion" *) Bit #(4) awregion, // in + (* port="awuser" *) Bit #(wd_user) awuser); // in + (* always_ready, result="awready" *) + method Bool m_awready; // out + + // Wr Data channel + (* always_ready, always_enabled, prefix = "" *) + method Action m_wvalid ((* port="wvalid" *) Bool wvalid, // in + (* port="wdata" *) Bit #(wd_data) wdata, // in + (* port="wstrb" *) Bit #(TDiv #(wd_data,8)) wstrb, // in + (* port="wlast" *) Bool wlast, // in + (* port="wuser" *) Bit #(wd_user) wuser); // in + (* always_ready, result="wready" *) + method Bool m_wready; // out + + // Wr Response channel + (* always_ready, result="bvalid" *) method Bool m_bvalid; // out + (* always_ready, result="bid" *) method Bit #(wd_id) m_bid; // out + (* always_ready, result="bresp" *) method Bit #(2) m_bresp; // out + (* always_ready, result="buser" *) method Bit #(wd_user) m_buser; // out + (* always_ready, always_enabled, prefix="" *) + method Action m_bready ((* port="bready" *) Bool bready); // in + + // Rd Addr channel + (* always_ready, always_enabled, prefix = "" *) + method Action m_arvalid ((* port="arvalid" *) Bool arvalid, // in + (* port="arid" *) Bit #(wd_id) arid, // in + (* port="araddr" *) Bit #(wd_addr) araddr, // in + (* port="arlen" *) Bit #(8) arlen, // in + (* port="arsize" *) AXI4_Size arsize, // in + (* port="arburst" *) Bit #(2) arburst, // in + (* port="arlock" *) Bit #(1) arlock, // in + (* port="arcache" *) Bit #(4) arcache, // in + (* port="arprot" *) Bit #(3) arprot, // in + (* port="arqos" *) Bit #(4) arqos, // in + (* port="arregion" *) Bit #(4) arregion, // in + (* port="aruser" *) Bit #(wd_user) aruser); // in + (* always_ready, result="arready" *) + method Bool m_arready; // out + + // Rd Data channel + (* always_ready, result="rvalid" *) method Bool m_rvalid; // out + (* always_ready, result="rid" *) method Bit #(wd_id) m_rid; // out + (* always_ready, result="rdata" *) method Bit #(wd_data) m_rdata; // out + (* always_ready, result="rresp" *) method Bit #(2) m_rresp; // out + (* always_ready, result="rlast" *) method Bool m_rlast; // out + (* always_ready, result="ruser" *) method Bit #(wd_user) m_ruser; // out + (* always_ready, always_enabled, prefix="" *) + method Action m_rready ((* port="rready" *) Bool rready); // in +endinterface: AXI4_RTL_S_IFC + +// ================================================================ +// Connecting RTL-level interfaces + +instance Connectable #(AXI4_RTL_M_IFC #(wd_id, wd_addr, wd_data, wd_user), + AXI4_RTL_S_IFC #(wd_id, wd_addr, wd_data, wd_user)); + + module mkConnection #(AXI4_RTL_M_IFC #(wd_id, wd_addr, wd_data, wd_user) axim, + AXI4_RTL_S_IFC #(wd_id, wd_addr, wd_data, wd_user) axis) + (Empty); + + (* fire_when_enabled, no_implicit_conditions *) + rule rl_AW; + axis.m_awvalid (axim.m_awvalid, + axim.m_awid, + axim.m_awaddr, + axim.m_awlen, + axim.m_awsize, + axim.m_awburst, + axim.m_awlock, + axim.m_awcache, + axim.m_awprot, + axim.m_awqos, + axim.m_awregion, + axim.m_awuser); + axim.m_awready (axis.m_awready); + endrule + + (* fire_when_enabled, no_implicit_conditions *) + rule rl_W; + axis.m_wvalid (axim.m_wvalid, + axim.m_wdata, + axim.m_wstrb, + axim.m_wlast, + axim.m_wuser); + axim.m_wready (axis.m_wready); + endrule + + (* fire_when_enabled, no_implicit_conditions *) + rule rl_B; + axim.m_bvalid (axis.m_bvalid, + axis.m_bid, + axis.m_bresp, + axis.m_buser); + axis.m_bready (axim.m_bready); + endrule + + (* fire_when_enabled, no_implicit_conditions *) + rule rl_AR; + axis.m_arvalid (axim.m_arvalid, + axim.m_arid, + axim.m_araddr, + axim.m_arlen, + axim.m_arsize, + axim.m_arburst, + axim.m_arlock, + axim.m_arcache, + axim.m_arprot, + axim.m_arqos, + axim.m_arregion, + axim.m_aruser); + axim.m_arready (axis.m_arready); + endrule + + (* fire_when_enabled, no_implicit_conditions *) + rule rl_R; + axim.m_rvalid (axis.m_rvalid, + axis.m_rid, + axis.m_rdata, + axis.m_rresp, + axis.m_rlast, + axis.m_ruser); + axis.m_rready (axim.m_rready); + endrule + endmodule +endinstance + +// ================================================================ +// AXI4 dummy M: never produces requests, never accepts responses + +AXI4_RTL_M_IFC #(wd_id, wd_addr, wd_data, wd_user) +dummy_AXI4_RTL_M_ifc += interface AXI4_RTL_M_IFC + // Wr Addr channel + method Bool m_awvalid = False; // out + method Bit #(wd_id) m_awid = ?; // out + method Bit #(wd_addr) m_awaddr = ?; // out + method Bit #(8) m_awlen = ?; // out + method AXI4_Size m_awsize = ?; // out + method Bit #(2) m_awburst = ?; // out + method Bit #(1) m_awlock = ?; // out + method Bit #(4) m_awcache = ?; // out + method Bit #(3) m_awprot = ?; // out + method Bit #(4) m_awqos = ?; // out + method Bit #(4) m_awregion = ?; // out + method Bit #(wd_user) m_awuser = ?; // out + method Action m_awready (Bool awready) = noAction; // in + + // Wr Data channel + method Bool m_wvalid = False; // out + method Bit #(wd_data) m_wdata = ?; // out + method Bit #(TDiv #(wd_data, 8)) m_wstrb = ?; // out + method Bool m_wlast = ?; // out + method Bit #(wd_user) m_wuser = ?; // out + + method Action m_wready (Bool wready) = noAction; // in + + // Wr Response channel + method Action m_bvalid (Bool bvalid, // in + Bit #(wd_id) bid, // in + Bit #(2) bresp, // in + Bit #(wd_user) buser); // in + noAction; + endmethod + method Bool m_bready = False; // out + + // Rd Addr channel + method Bool m_arvalid = False; // out + method Bit #(wd_id) m_arid = ?; // out + method Bit #(wd_addr) m_araddr = ?; // out + method Bit #(8) m_arlen = ?; // out + method AXI4_Size m_arsize = ?; // out + method Bit #(2) m_arburst = ?; // out + method Bit #(1) m_arlock = ?; // out + method Bit #(4) m_arcache = ?; // out + method Bit #(3) m_arprot = ?; // out + method Bit #(4) m_arqos = ?; // out + method Bit #(4) m_arregion = ?; // out + method Bit #(wd_user) m_aruser = ?; // out + method Action m_arready (Bool arready) = noAction; // in + + // Rd Data channel + method Action m_rvalid (Bool rvalid, // in + Bit #(wd_id) rid, // in + Bit #(wd_data) rdata, // in + Bit #(2) rresp, // in + Bool rlast, // in + Bit #(wd_user) ruser); // in + noAction; + endmethod + method Bool m_rready = False; // out + endinterface; + +// ================================================================ +// AXI4 dummy S: never accepts requests, never produces responses + +AXI4_RTL_S_IFC #(wd_id, wd_addr, wd_data, wd_user) +dummy_AXI4_RTL_S_ifc += interface AXI4_RTL_S_IFC + // Wr Addr channel + method Action m_awvalid (Bool awvalid, + Bit #(wd_id) awid, + Bit #(wd_addr) awaddr, + Bit #(8) awlen, + AXI4_Size awsize, + Bit #(2) awburst, + Bit #(1) awlock, + Bit #(4) awcache, + Bit #(3) awprot, + Bit #(4) awqos, + Bit #(4) awregion, + Bit #(wd_user) awuser); + noAction; + endmethod + + method Bool m_awready; + return False; + endmethod + + // Wr Data channel + method Action m_wvalid (Bool wvalid, + Bit #(wd_data) wdata, + Bit #(TDiv #(wd_data, 8)) wstrb, + Bool wlast, + Bit #(wd_user) wuser); + noAction; + endmethod + + method Bool m_wready; + return False; + endmethod + + // Wr Response channel + method Bool m_bvalid; + return False; + endmethod + + method Bit #(wd_id) m_bid; + return ?; + endmethod + + method Bit #(2) m_bresp; + return 0; + endmethod + + method Bit #(wd_user) m_buser; + return ?; + endmethod + + method Action m_bready (Bool bready); + noAction; + endmethod + + // Rd Addr channel + method Action m_arvalid (Bool arvalid, + Bit #(wd_id) arid, + Bit #(wd_addr) araddr, + Bit #(8) arlen, + AXI4_Size arsize, + Bit #(2) arburst, + Bit #(1) arlock, + Bit #(4) arcache, + Bit #(3) arprot, + Bit #(4) arqos, + Bit #(4) arregion, + Bit #(wd_user) aruser); + noAction; + endmethod + + method Bool m_arready; + return False; + endmethod + + // Rd Data channel + method Bool m_rvalid; + return False; + endmethod + + method Bit #(wd_id) m_rid; + return 0; + endmethod + + method Bit #(wd_data) m_rdata; + return 0; + endmethod + + method Bit #(2) m_rresp; + return 0; + endmethod + + method Bool m_rlast; + return True; + endmethod + + method Bit #(wd_user) m_ruser; + return ?; + endmethod + + method Action m_rready (Bool rready); + noAction; + endmethod + endinterface; + +// **************************************************************** + +interface AXI4_BSV_to_RTL_IFC #(numeric type wd_id, + numeric type wd_addr, + numeric type wd_data, + numeric type wd_user); + interface AXI4_S_IFC #(wd_id, wd_addr, wd_data, wd_user) ifc_S; + interface AXI4_RTL_M_IFC #(wd_id, wd_addr, wd_data, wd_user) rtl_M; +endinterface: AXI4_BSV_to_RTL_IFC + +interface AXI4_RTL_to_BSV_IFC #(numeric type wd_id, + numeric type wd_addr, + numeric type wd_data, + numeric type wd_user); + interface AXI4_RTL_S_IFC #(wd_id, wd_addr, wd_data, wd_user) rtl_S; + interface AXI4_M_IFC #(wd_id, wd_addr, wd_data, wd_user) ifc_M; +endinterface: AXI4_RTL_to_BSV_IFC + +// ================================================================ +// M transactor +// This version uses FIFOFs for total decoupling. + +module mkAXI4_BSV_to_RTL (AXI4_BSV_to_RTL_IFC #(wd_id, wd_addr, wd_data, wd_user)); + + Bool unguarded = True; + Bool guarded = False; + + // These FIFOs are guarded on BSV side, unguarded on AXI side + FIFOF #(AXI4_AW #(wd_id, wd_addr, wd_user)) f_AW <- mkGFIFOF (guarded, unguarded); + FIFOF #(AXI4_W #(wd_data, wd_user)) f_W <- mkGFIFOF (guarded, unguarded); + FIFOF #(AXI4_B #(wd_id, wd_user)) f_B <- mkGFIFOF (unguarded, guarded); + + FIFOF #(AXI4_AR #(wd_id, wd_addr, wd_user)) f_AR <- mkGFIFOF (guarded, unguarded); + FIFOF #(AXI4_R #(wd_id, wd_data, wd_user)) f_R <- mkGFIFOF (unguarded, guarded); + + // ---------------------------------------------------------------- + // INTERFACE + + // RTL side + interface AXI4_RTL_M_IFC rtl_M; + // Wr Addr channel + method Bool m_awvalid = f_AW.notEmpty; + method Bit #(wd_id) m_awid = f_AW.first.awid; + method Bit #(wd_addr) m_awaddr = f_AW.first.awaddr; + method Bit #(8) m_awlen = f_AW.first.awlen; + method AXI4_Size m_awsize = f_AW.first.awsize; + method Bit #(2) m_awburst = f_AW.first.awburst; + method Bit #(1) m_awlock = f_AW.first.awlock; + method Bit #(4) m_awcache = f_AW.first.awcache; + method Bit #(3) m_awprot = f_AW.first.awprot; + method Bit #(4) m_awqos = f_AW.first.awqos; + method Bit #(4) m_awregion = f_AW.first.awregion; + method Bit #(wd_user) m_awuser = f_AW.first.awuser; + method Action m_awready (Bool awready); + if (f_AW.notEmpty && awready) f_AW.deq; + endmethod + + // Wr Data channel + method Bool m_wvalid = f_W.notEmpty; + method Bit #(wd_data) m_wdata = f_W.first.wdata; + method Bit #(TDiv #(wd_data, 8)) m_wstrb = f_W.first.wstrb; + method Bool m_wlast = f_W.first.wlast; + method Bit #(wd_user) m_wuser = f_W.first.wuser; + method Action m_wready (Bool wready); + if (f_W.notEmpty && wready) f_W.deq; + endmethod + + // Wr Response channel + method Action m_bvalid (Bool bvalid, + Bit #(wd_id) bid, + Bit #(2) bresp, + Bit #(wd_user) buser); + if (bvalid && f_B.notFull) + f_B.enq (AXI4_B {bid: bid, + bresp: bresp, + buser: buser}); + endmethod + + method Bool m_bready; + return f_B.notFull; + endmethod + + // Rd Addr channel + method Bool m_arvalid = f_AR.notEmpty; + method Bit #(wd_id) m_arid = f_AR.first.arid; + method Bit #(wd_addr) m_araddr = f_AR.first.araddr; + method Bit #(8) m_arlen = f_AR.first.arlen; + method AXI4_Size m_arsize = f_AR.first.arsize; + method Bit #(2) m_arburst = f_AR.first.arburst; + method Bit #(1) m_arlock = f_AR.first.arlock; + method Bit #(4) m_arcache = f_AR.first.arcache; + method Bit #(3) m_arprot = f_AR.first.arprot; + method Bit #(4) m_arqos = f_AR.first.arqos; + method Bit #(4) m_arregion = f_AR.first.arregion; + method Bit #(wd_user) m_aruser = f_AR.first.aruser; + + method Action m_arready (Bool arready); + if (f_AR.notEmpty && arready) f_AR.deq; + endmethod + + // Rd Data channel + method Action m_rvalid (Bool rvalid, // in + Bit #(wd_id) rid, // in + Bit #(wd_data) rdata, // in + Bit #(2) rresp, // in + Bool rlast, // in + Bit #(wd_user) ruser); // in + if (rvalid && f_R.notFull) + f_R.enq (AXI4_R {rid: rid, + rdata: rdata, + rresp: rresp, + rlast: rlast, + ruser: ruser}); + endmethod + + method Bool m_rready; + return f_R.notFull; + endmethod + + endinterface + + // BSV side + interface AXI4_S_IFC ifc_S; + interface i_AW = to_FIFOF_I (f_AW); + interface i_W = to_FIFOF_I (f_W); + interface o_B = to_FIFOF_O (f_B); + + interface i_AR = to_FIFOF_I (f_AR); + interface o_R = to_FIFOF_O (f_R); + endinterface +endmodule: mkAXI4_BSV_to_RTL + +// ================================================================ +// This version uses FIFOFs for total decoupling. + +module mkAXI4_RTL_to_BSV (AXI4_RTL_to_BSV_IFC #(wd_id, wd_addr, wd_data, wd_user)); + + Bool unguarded = True; + Bool guarded = False; + + // These FIFOs are guarded on BSV side, unguarded on AXI side + FIFOF #(AXI4_AW #(wd_id, wd_addr, wd_user)) f_AW <- mkGFIFOF (unguarded, guarded); + FIFOF #(AXI4_W #(wd_data, wd_user)) f_W <- mkGFIFOF (unguarded, guarded); + FIFOF #(AXI4_B #(wd_id, wd_user)) f_B <- mkGFIFOF (guarded, unguarded); + + FIFOF #(AXI4_AR #(wd_id, wd_addr, wd_user)) f_AR <- mkGFIFOF (unguarded, guarded); + FIFOF #(AXI4_R #(wd_id, wd_data, wd_user)) f_R <- mkGFIFOF (guarded, unguarded); + + // ---------------------------------------------------------------- + // INTERFACE + + // AXI side + interface AXI4_RTL_S_IFC rtl_S; + // Wr Addr channel + method Action m_awvalid (Bool awvalid, + Bit #(wd_id) awid, + Bit #(wd_addr) awaddr, + Bit #(8) awlen, + AXI4_Size awsize, + Bit #(2) awburst, + Bit #(1) awlock, + Bit #(4) awcache, + Bit #(3) awprot, + Bit #(4) awqos, + Bit #(4) awregion, + Bit #(wd_user) awuser); + if (awvalid && f_AW.notFull) + f_AW.enq (AXI4_AW {awid: awid, + awaddr: awaddr, + awlen: awlen, + awsize: awsize, + awburst: awburst, + awlock: awlock, + awcache: awcache, + awprot: awprot, + awqos: awqos, + awregion: awregion, + awuser: awuser}); + endmethod + + method Bool m_awready; + return f_AW.notFull; + endmethod + + // Wr Data channel + method Action m_wvalid (Bool wvalid, + Bit #(wd_data) wdata, + Bit #(TDiv #(wd_data, 8)) wstrb, + Bool wlast, + Bit #(wd_user) wuser); + if (wvalid && f_W.notFull) + f_W.enq (AXI4_W {wdata: wdata, + wstrb: wstrb, + wlast: wlast, + wuser: wuser}); + endmethod + + method Bool m_wready; + return f_W.notFull; + endmethod + + // Wr Response channel + method Bool m_bvalid = f_B.notEmpty; + method Bit #(wd_id) m_bid = f_B.first.bid; + method Bit #(2) m_bresp = f_B.first.bresp; + method Bit #(wd_user) m_buser = f_B.first.buser; + method Action m_bready (Bool bready); + if (bready && f_B.notEmpty) + f_B.deq; + endmethod + + // Rd Addr channel + method Action m_arvalid (Bool arvalid, + Bit #(wd_id) arid, + Bit #(wd_addr) araddr, + Bit #(8) arlen, + AXI4_Size arsize, + Bit #(2) arburst, + Bit #(1) arlock, + Bit #(4) arcache, + Bit #(3) arprot, + Bit #(4) arqos, + Bit #(4) arregion, + Bit #(wd_user) aruser); + if (arvalid && f_AR.notFull) + f_AR.enq (AXI4_AR {arid: arid, + araddr: araddr, + arlen: arlen, + arsize: arsize, + arburst: arburst, + arlock: arlock, + arcache: arcache, + arprot: arprot, + arqos: arqos, + arregion: arregion, + aruser: aruser}); + endmethod + + method Bool m_arready; + return f_AR.notFull; + endmethod + + // Rd Data channel + method Bool m_rvalid = f_R.notEmpty; + method Bit #(wd_id) m_rid = f_R.first.rid; + method Bit #(wd_data) m_rdata = f_R.first.rdata; + method Bit #(2) m_rresp = f_R.first.rresp; + method Bool m_rlast = f_R.first.rlast; + method Bit #(wd_user) m_ruser = f_R.first.ruser; + method Action m_rready (Bool rready); + if (rready && f_R.notEmpty) + f_R.deq; + endmethod + endinterface + + // BSV side + interface AXI4_M_IFC ifc_M; + interface o_AW = to_FIFOF_O (f_AW); + interface o_W = to_FIFOF_O (f_W); + interface i_B = to_FIFOF_I (f_B); + + interface o_AR = to_FIFOF_O (f_AR); + interface i_R = to_FIFOF_I (f_R); + endinterface +endmodule: mkAXI4_RTL_to_BSV + +// ================================================================ +// Help function: fn_crg_and_rg_to_FIFOF_I +// In the modules below, we use a crg_full and a rg_data to represent a fifo. +// These functions convert these to FIFOF_I and FIFOF_O interfaces. + +function FIFOF_I #(t) fn_crg_and_rg_to_FIFOF_I (Reg #(Bool) rg_full, Reg #(t) rg_data); + return interface FIFOF_I; + method Action enq (t x) if (! rg_full); + rg_full <= True; + rg_data <= x; + endmethod + method Bool notFull; + return (! rg_full); + endmethod + endinterface; +endfunction + +function FIFOF_O #(t) fn_crg_and_rg_to_FIFOF_O (Reg #(Bool) rg_full, Reg #(t) rg_data); + return interface FIFOF_O; + method t first () if (rg_full); + return rg_data; + endmethod + method Action deq () if (rg_full); + rg_full <= False; + endmethod + method notEmpty; + return rg_full; + endmethod + endinterface; +endfunction + +// ================================================================ +// M transactor +// This version uses crgs and regs instead of FIFOFs. +// This uses 1/2 the resources, but introduces scheduling dependencies. + +module mkAXI4_BSV_to_RTL_2 (AXI4_BSV_to_RTL_IFC #(wd_id, wd_addr, wd_data, wd_user)); + + // Each crg_full, rg_data pair below represents a 1-element fifo. + + Array #(Reg #(Bool)) crg_AW_full <- mkCReg (3, False); + Reg #(AXI4_AW #(wd_id, wd_addr, wd_user)) rg_AW <- mkRegU; + + Array #(Reg #(Bool)) crg_W_full <- mkCReg (3, False); + Reg #(AXI4_W #(wd_data, wd_user)) rg_W <- mkRegU; + + Array #(Reg #(Bool)) crg_B_full <- mkCReg (3, False); + Reg #(AXI4_B #(wd_id, wd_user)) rg_B <- mkRegU; + + Array #(Reg #(Bool)) crg_AR_full <- mkCReg (3, False); + Reg #(AXI4_AR #(wd_id, wd_addr, wd_user)) rg_AR <- mkRegU; + + Array #(Reg #(Bool)) crg_R_full <- mkCReg (3, False); + Reg #(AXI4_R #(wd_id, wd_data, wd_user)) rg_R <- mkRegU; + + // The following CReg port indexes specify the relative scheduling of: + // {first,deq,notEmpty} {enq,notFull} clear + + // TODO: 'deq/enq/clear = 1/2/0' is unusual, but eliminates a + // scheduling cycle in Piccolo's DCache. Normally should be 0/1/2. + + Integer port_deq = 1; + Integer port_enq = 2; + Integer port_clear = 0; + + // ---------------------------------------------------------------- + // INTERFACE + + // RTL side + interface AXI4_RTL_M_IFC rtl_M; + // Wr Addr channel + method Bool m_awvalid = crg_AW_full [port_deq]; + method Bit #(wd_id) m_awid = rg_AW.awid; + method Bit #(wd_addr) m_awaddr = rg_AW.awaddr; + method Bit #(8) m_awlen = rg_AW.awlen; + method AXI4_Size m_awsize = rg_AW.awsize; + method Bit #(2) m_awburst = rg_AW.awburst; + method Bit #(1) m_awlock = rg_AW.awlock; + method Bit #(4) m_awcache = rg_AW.awcache; + method Bit #(3) m_awprot = rg_AW.awprot; + method Bit #(4) m_awqos = rg_AW.awqos; + method Bit #(4) m_awregion = rg_AW.awregion; + method Bit #(wd_user) m_awuser = rg_AW.awuser; + method Action m_awready (Bool awready); + if (crg_AW_full [port_deq] && awready) + crg_AW_full [port_deq] <= False; // deq + endmethod + + // Wr Data channel + method Bool m_wvalid = crg_W_full [port_deq]; + method Bit #(wd_data) m_wdata = rg_W.wdata; + method Bit #(TDiv #(wd_data, 8)) m_wstrb = rg_W.wstrb; + method Bool m_wlast = rg_W.wlast; + method Bit #(wd_user) m_wuser = rg_W.wuser; + method Action m_wready (Bool wready); + if (crg_W_full [port_deq] && wready) + crg_W_full [port_deq] <= False; + endmethod + + // Wr Response channel + method Action m_bvalid (Bool bvalid, + Bit #(wd_id) bid, + Bit #(2) bresp, + Bit #(wd_user) buser); + if (bvalid && (! (crg_B_full [port_enq]))) begin + crg_B_full [port_enq] <= True; + rg_B <= AXI4_B {bid: bid, + bresp: bresp, + buser: buser}; + end + endmethod + + method Bool m_bready; + return (! (crg_B_full [port_enq])); + endmethod + + // Rd Addr channel + method Bool m_arvalid = crg_AR_full [port_deq]; + method Bit #(wd_id) m_arid = rg_AR.arid; + method Bit #(wd_addr) m_araddr = rg_AR.araddr; + method Bit #(8) m_arlen = rg_AR.arlen; + method AXI4_Size m_arsize = rg_AR.arsize; + method Bit #(2) m_arburst = rg_AR.arburst; + method Bit #(1) m_arlock = rg_AR.arlock; + method Bit #(4) m_arcache = rg_AR.arcache; + method Bit #(3) m_arprot = rg_AR.arprot; + method Bit #(4) m_arqos = rg_AR.arqos; + method Bit #(4) m_arregion = rg_AR.arregion; + method Bit #(wd_user) m_aruser = rg_AR.aruser; + method Action m_arready (Bool arready); + if (crg_AR_full [port_deq] && arready) + crg_AR_full [port_deq] <= False; // deq + endmethod + + // Rd Data channel + method Action m_rvalid (Bool rvalid, + Bit #(wd_id) rid, + Bit #(wd_data) rdata, + Bit #(2) rresp, + Bool rlast, + Bit #(wd_user) ruser); + if (rvalid && (! (crg_R_full [port_enq]))) + crg_R_full [port_enq] <= True; + rg_R <= (AXI4_R {rid: rid, + rdata: rdata, + rresp: rresp, + rlast: rlast, + ruser: ruser}); + endmethod + + method Bool m_rready; + return (! (crg_R_full [port_enq])); + endmethod + + endinterface + + // BSV side + interface AXI4_S_IFC ifc_S; + interface i_AW = fn_crg_and_rg_to_FIFOF_I (crg_AW_full [port_enq], rg_AW); + interface i_W = fn_crg_and_rg_to_FIFOF_I (crg_W_full [port_enq], rg_W); + interface o_B = fn_crg_and_rg_to_FIFOF_O (crg_B_full [port_deq], rg_B); + + interface i_AR = fn_crg_and_rg_to_FIFOF_I (crg_AR_full [port_enq], rg_AR); + interface o_R = fn_crg_and_rg_to_FIFOF_O (crg_R_full [port_deq], rg_R); + endinterface +endmodule: mkAXI4_BSV_to_RTL_2 + +// ---------------------------------------------------------------- +// S transactor +// This version uses crgs and regs instead of FIFOFs. +// This uses 1/2 the resources, but introduces scheduling dependencies. + +module mkAXI4_RTL_to_BSV_2 (AXI4_RTL_to_BSV_IFC #(wd_id, wd_addr, wd_data, wd_user)); + + // Each crg_full, rg_data pair below represents a 1-element fifo. + + // These FIFOs are guarded on BSV side, unguarded on AXI side + Array #(Reg #(Bool)) crg_AW_full <- mkCReg (3, False); + Reg #(AXI4_AW #(wd_id, wd_addr, wd_user)) rg_AW <- mkRegU; + + Array #(Reg #(Bool)) crg_W_full <- mkCReg (3, False); + Reg #(AXI4_W #(wd_data, wd_user)) rg_W <- mkRegU; + + Array #(Reg #(Bool)) crg_B_full <- mkCReg (3, False); + Reg #(AXI4_B #(wd_id, wd_user)) rg_B <- mkRegU; + + Array #(Reg #(Bool)) crg_AR_full <- mkCReg (3, False); + Reg #(AXI4_AR #(wd_id, wd_addr, wd_user)) rg_AR <- mkRegU; + + Array #(Reg #(Bool)) crg_R_full <- mkCReg (3, False); + Reg #(AXI4_R #(wd_id, wd_data, wd_user)) rg_R <- mkRegU; + + // The following CReg port indexes specify the relative scheduling of: + // {first,deq,notEmpty} {enq,notFull} clear + Integer port_deq = 0; + Integer port_enq = 1; + Integer port_clear = 2; + + // ---------------------------------------------------------------- + // INTERFACE + + // RTL side + interface AXI4_RTL_S_IFC rtl_S; + // Wr Addr channel + method Action m_awvalid (Bool awvalid, + Bit #(wd_id) awid, + Bit #(wd_addr) awaddr, + Bit #(8) awlen, + AXI4_Size awsize, + Bit #(2) awburst, + Bit #(1) awlock, + Bit #(4) awcache, + Bit #(3) awprot, + Bit #(4) awqos, + Bit #(4) awregion, + Bit #(wd_user) awuser); + + if (awvalid && (! crg_AW_full [port_enq])) begin + crg_AW_full [port_enq] <= True; // enq + rg_AW <= AXI4_AW {awid: awid, + awaddr: awaddr, + awlen: awlen, + awsize: awsize, + awburst: awburst, + awlock: awlock, + awcache: awcache, + awprot: awprot, + awqos: awqos, + awregion: awregion, + awuser: awuser}; + end + endmethod + + method Bool m_awready; + return (! crg_AW_full [port_enq]); + endmethod + + // Wr Data channel + method Action m_wvalid (Bool wvalid, + Bit #(wd_data) wdata, + Bit #(TDiv #(wd_data, 8)) wstrb, + Bool wlast, + Bit #(wd_user) wuser); + if (wvalid && (! crg_W_full [port_enq])) begin + crg_W_full [port_enq] <= True; // enq + rg_W <= AXI4_W {wdata: wdata, + wstrb: wstrb, + wlast: wlast, + wuser: wuser}; + end + endmethod + + method Bool m_wready; + return (! crg_W_full [port_enq]); + endmethod + + // Wr Response channel + method Bool m_bvalid = crg_B_full [port_deq]; + method Bit #(wd_id) m_bid = rg_B.bid; + method Bit #(2) m_bresp = rg_B.bresp; + method Bit #(wd_user) m_buser = rg_B.buser; + method Action m_bready (Bool bready); + if (bready && crg_B_full [port_deq]) + crg_B_full [port_deq] <= False; // deq + endmethod + + // Rd Addr channel + method Action m_arvalid (Bool arvalid, + Bit #(wd_id) arid, + Bit #(wd_addr) araddr, + Bit #(8) arlen, + AXI4_Size arsize, + Bit #(2) arburst, + Bit #(1) arlock, + Bit #(4) arcache, + Bit #(3) arprot, + Bit #(4) arqos, + Bit #(4) arregion, + Bit #(wd_user) aruser); + if (arvalid && (! crg_AR_full [port_enq])) begin + crg_AR_full [port_enq] <= True; // enq + rg_AR <= AXI4_AR {arid: arid, + araddr: araddr, + arlen: arlen, + arsize: arsize, + arburst: arburst, + arlock: arlock, + arcache: arcache, + arprot: arprot, + arqos: arqos, + arregion: arregion, + aruser: aruser}; + end + endmethod + + method Bool m_arready; + return (! crg_AR_full [port_enq]); + endmethod + + // Rd Data channel + method Bool m_rvalid = crg_R_full [port_deq]; + method Bit #(wd_id) m_rid = rg_R.rid; + method Bit #(wd_data) m_rdata = rg_R.rdata; + method Bit #(2) m_rresp = rg_R.rresp; + method Bool m_rlast = rg_R.rlast; + method Bit #(wd_user) m_ruser = rg_R.ruser; + method Action m_rready (Bool rready); + if (rready && crg_R_full [port_deq]) + crg_R_full [port_deq] <= False; // deq + endmethod + endinterface + + // BSV side + interface AXI4_M_IFC ifc_M; + interface o_AW = fn_crg_and_rg_to_FIFOF_O (crg_AW_full [port_deq], rg_AW); + interface o_W = fn_crg_and_rg_to_FIFOF_O (crg_W_full [port_deq], rg_W); + interface i_B = fn_crg_and_rg_to_FIFOF_I (crg_B_full [port_enq], rg_B); + + interface o_AR = fn_crg_and_rg_to_FIFOF_O (crg_AR_full [port_deq], rg_AR); + interface i_R = fn_crg_and_rg_to_FIFOF_I (crg_R_full [port_enq], rg_R); + endinterface +endmodule: mkAXI4_RTL_to_BSV_2 + +// **************************************************************** + +endpackage diff --git a/Libraries/AMBA_Fabrics/AXI4/AXI4_ClockCrossing.bsv b/Libraries/AMBA_Fabrics/AXI4/AXI4_ClockCrossing.bsv deleted file mode 100644 index 1a3ad30..0000000 --- a/Libraries/AMBA_Fabrics/AXI4/AXI4_ClockCrossing.bsv +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright (c) 2020 Bluespec, Inc. All Rights Reserved -// -// SPDX-License-Identifier: BSD-3-Clause - -package AXI4_ClockCrossing; - -import Clocks ::*; -import AXI4_Types ::*; -import AXI4_Extra_Xactors ::*; -import Connectable ::*; -import Semi_FIFOF ::*; -import GetPut ::*; - -// ================================================================ - -interface AXI4_ClockCrossing_IFC #(numeric type id_, - numeric type addr_, - numeric type data_, - numeric type user_); - interface AXI4_S_IFC #(id_, addr_, data_, user_) from_M; - interface AXI4_M_IFC #(id_, addr_, data_, user_) to_S; -endinterface - -// ================================================================ - -module mkAXI4_ClockCrossing #(Clock clock_M, - Reset reset_M, - Clock clock_S, - Reset reset_S) - (AXI4_ClockCrossing_IFC #(id_, addr_, data_, user_)); - - SyncFIFOIfc #(AXI4_Wr_Addr #(id_, addr_, user_)) - f_aw <- mkSyncFIFO (4, clock_M, reset_M, clock_S); - - SyncFIFOIfc #(AXI4_Wr_Data #(data_, user_)) - f_w <- mkSyncFIFO (4, clock_M, reset_M, clock_S); - - SyncFIFOIfc #(AXI4_Wr_Resp #(id_, user_)) - f_b <- mkSyncFIFO (4, clock_S, reset_S, clock_M); - - SyncFIFOIfc #(AXI4_Rd_Addr #(id_, addr_, user_)) - f_ar <- mkSyncFIFO (4, clock_M, reset_M, clock_S); - - SyncFIFOIfc #(AXI4_Rd_Data #(id_, data_, user_)) - f_r <- mkSyncFIFO (4, clock_S, reset_S, clock_M); - - AXI4_S_IFC #(id_, addr_, data_, user_) - s_xactor <- mkAXI4_S_Xactor_3 (f_aw, f_w, f_b, f_ar, f_r, - clocked_by clock_M, - reset_by reset_M); - - AXI4_M_IFC #(id_, addr_, data_, user_) - m_xactor <- mkAXI4_M_Xactor_3 (f_aw, f_w, f_b, f_ar, f_r, - clocked_by clock_S, - reset_by reset_S); - - interface AXI4_S_IFC from_M = s_xactor; - interface AXI4_M_IFC to_S = m_xactor; -endmodule - -// ---------------------------------------------------------------- -// Same as above, with S-side using current-clock - -module mkAXI4_ClockCrossingToCC #(Clock clock_M, Reset reset_M) - (AXI4_ClockCrossing_IFC #(id_, addr_, data_, user_)); - let clock_S <- exposeCurrentClock; - let reset_S <- exposeCurrentReset; - let crossing <- mkAXI4_ClockCrossing (clock_M, reset_M, clock_S, reset_S); - - return crossing; -endmodule - -endpackage diff --git a/Libraries/AMBA_Fabrics/AXI4/AXI4_Clock_Crossers.bsv b/Libraries/AMBA_Fabrics/AXI4/AXI4_Clock_Crossers.bsv index 31e3d64..0419801 100644 --- a/Libraries/AMBA_Fabrics/AXI4/AXI4_Clock_Crossers.bsv +++ b/Libraries/AMBA_Fabrics/AXI4/AXI4_Clock_Crossers.bsv @@ -1,7 +1,7 @@ -// Copyright (c) 2022 Bluespec, Inc. All Rights Reserved +// Copyright (c) 2022-2024 Bluespec, Inc. All Rights Reserved // Author: Rishiyur S. Nikhil -// (original code for mkAXI4_Clock_Crossing from Joe Stoy) -// +// (adapted and renamed from original code from Joe Stoy) + // SPDX-License-Identifier: BSD-3-Clause package AXI4_Clock_Crossers; @@ -10,19 +10,19 @@ package AXI4_Clock_Crossers; // This package defines clock-domain-crossing modules for AXI4 M and S // interfaces: -// Clock -> Clock -> AXI4_M_IFC -> Module #(AXI4_M_IFC) +// Clock -> Clock -> AXI4_RTL_M_IFC -> Module #(AXI4_RTL_M_IFC) export mkAXI4_M_Clock_Crosser; -// Clock -> Clock -> AXI4_S_IFC -> Module #(AXI4_S_IFC) +// Clock -> Clock -> AXI4_RTL_S_IFC -> Module #(AXI4_RTL_S_IFC) export mkAXI4_S_Clock_Crosser; -export AXI4_ClockCrossing_IFC (..); +export AXI4_SyncBuffer_IFC (..); // Clock1 -> Clock2 -> Module #(AXI4_Clock_Crossing_IFC) -export mkAXI4_ClockCrossing; +export mkAXI4_SyncBuffer; // Clock1 -> Module #(AXI4_Clock_Crossing_IFC) -export mkAXI4_ClockCrossingToCC; +export mkAXI4_SyncBufferToCC; // ================================================================ // Bluespec library imports @@ -31,7 +31,7 @@ import Clocks :: *; import Connectable :: *; // ---------------- -// BSV additional libs +// Bluespec misc. libs import Cur_Cycle :: *; import GetPut_Aux :: *; @@ -40,178 +40,139 @@ import Semi_FIFOF :: *; // ================================================================ // Project imports -import AXI4_Types :: *; -import AXI_SyncBuffer :: *; -import AXI4_Xactors :: *; +import AXI4_Types :: *; +import AXI4_BSV_RTL :: *; +import AXIx_SyncBuffer :: *; // ================================================================ -// Clock1 -> Clock2 -> AXI4_S_IFC_1 -> Module #(AXI4_S_IFC_2) -// Clock1 should be same clock as axi4_S_1 -// Clock2 is clock of result axi4_S_2 - -module mkAXI4_S_Clock_Crosser - #(Integer depth, - Clock clk1, Reset rst1, - Clock clk2, Reset rst2, - AXI4_S_IFC #(wd_id, wd_addr, wd_data, wd_user) axi4_S_1) - (AXI4_S_IFC #(wd_id, wd_addr, wd_data, wd_user)); - - // Transactor towards ifc1 - AXI4_M_Xactor_IFC #(wd_id, wd_addr, wd_data, wd_user) - axi4_M_xactor <- mkAXI4_M_Xactor (clocked_by clk1, reset_by rst1); - - // Syncbuffer between transactors - AXI_SyncBuffer_IFC #(AXI4_Wr_Addr #(wd_id, wd_addr, wd_user), - AXI4_Wr_Data #(wd_data, wd_user), - AXI4_Wr_Resp #(wd_id, wd_user), - AXI4_Rd_Addr #(wd_id, wd_addr, wd_user), - AXI4_Rd_Data #(wd_id, wd_data, wd_user)) - axi4_syncbuf <- mkAXI_SyncBuffer (depth, clk2, rst2, clk1, rst1); - - // Transactor with ifc2 - AXI4_S_Xactor_IFC #(wd_id, wd_addr, wd_data, wd_user) - axi4_S_xactor <- mkAXI4_S_Xactor (clocked_by clk2, reset_by rst2); +// Function to transform an AXI4_S_IFC with a clock crosser +// Clock1 -> Clock2 -> AXI4_S_IFC (on clock2) -> Module #(AXI4_S_IFC (on clock 1)) +// Clock1 is the upstream clock +// Clock2 is the downstream clock + +module mkAXI4_S_Clock_Crosser #(Integer depth, + Clock clk1, Reset rst1, + Clock clk2, Reset rst2, + AXI4_S_IFC #(wd_id, wd_addr, wd_data, wd_user) ifc_S) + (AXI4_S_IFC #(wd_id, wd_addr, wd_data, wd_user)); + + AXIx_SyncBuffer_IFC #(AXI4_AW #(wd_id, wd_addr, wd_user), + AXI4_W #(wd_data, wd_user), + AXI4_B #(wd_id, wd_user), + AXI4_AR #(wd_id, wd_addr, wd_user), + AXI4_R #(wd_id, wd_data, wd_user)) + axi4_syncbuf <- mkAXIx_SyncBuffer (depth, clk1, rst1, clk2, rst2); // ---------------- - mkConnection (axi4_M_xactor.axi_side, axi4_S_1); - - mkConnection (axi4_M_xactor.i_wr_addr, axi4_syncbuf.to_S.o_aw); - mkConnection (axi4_M_xactor.i_wr_data, axi4_syncbuf.to_S.o_w); - mkConnection (axi4_M_xactor.o_wr_resp, axi4_syncbuf.to_S.i_b); - mkConnection (axi4_M_xactor.i_rd_addr, axi4_syncbuf.to_S.o_ar); - mkConnection (axi4_M_xactor.o_rd_data, axi4_syncbuf.to_S.i_r); - - mkConnection (axi4_syncbuf.from_M.i_aw, axi4_S_xactor.o_wr_addr); - mkConnection (axi4_syncbuf.from_M.i_w, axi4_S_xactor.o_wr_data); - mkConnection (axi4_syncbuf.from_M.o_b, axi4_S_xactor.i_wr_resp); - mkConnection (axi4_syncbuf.from_M.i_ar, axi4_S_xactor.o_rd_addr); - mkConnection (axi4_syncbuf.from_M.o_r, axi4_S_xactor.i_rd_data); + mkConnection (axi4_syncbuf.to_S.o_aw, ifc_S.i_AW); + mkConnection (axi4_syncbuf.to_S.o_w, ifc_S.i_W); + mkConnection (axi4_syncbuf.to_S.i_b, ifc_S.o_B); + mkConnection (axi4_syncbuf.to_S.o_ar, ifc_S.i_AR); + mkConnection (axi4_syncbuf.to_S.i_r, ifc_S.o_R); // ---------------- // INTERFACE - let axi4_S_2 = axi4_S_xactor.axi_side; - return axi4_S_2; + interface i_AW = axi4_syncbuf.from_M.i_aw; + interface i_W = axi4_syncbuf.from_M.i_w; + interface o_B = axi4_syncbuf.from_M.o_b; + interface i_AR = axi4_syncbuf.from_M.i_ar; + interface o_R = axi4_syncbuf.from_M.o_r; endmodule // ================================================================ -// Clock1 -> Clock2 -> AXI4_M_IFC_1 -> Module #(AXI4_M_IFC_2) -// Clock1 should be same clock as axi4_M_1 -// Clock2 is clock of result axi4_M_2 - -module mkAXI4_M_Clock_Crosser - #(Integer depth, - Clock clk1, Reset rst1, - Clock clk2, Reset rst2, - AXI4_M_IFC #(wd_id, wd_addr, wd_data, wd_user) axi4_M_1) - (AXI4_M_IFC #(wd_id, wd_addr, wd_data, wd_user)); +// Function to transform an AXI4_M_IFC with a clock crosser +// Clock1 -> Clock2 -> AXI4_M_IFC (on clock1) -> Module #(AXI4_M_IFC (on clock 2)) +// Clock1 is the upstream clock +// Clock2 is the downstream clock - // Transactor towards ifc1 - AXI4_S_Xactor_IFC #(wd_id, wd_addr, wd_data, wd_user) - axi4_S_xactor <- mkAXI4_S_Xactor (clocked_by clk1, reset_by rst1); +module mkAXI4_M_Clock_Crosser #(Integer depth, + Clock clk1, Reset rst1, + Clock clk2, Reset rst2, + AXI4_M_IFC #(wd_id, wd_addr, wd_data, wd_user) ifc_M) + (AXI4_M_IFC #(wd_id, wd_addr, wd_data, wd_user)); // Syncbuffer between transactors - AXI_SyncBuffer_IFC #(AXI4_Wr_Addr #(wd_id, wd_addr, wd_user), - AXI4_Wr_Data #(wd_data, wd_user), - AXI4_Wr_Resp #(wd_id, wd_user), - AXI4_Rd_Addr #(wd_id, wd_addr, wd_user), - AXI4_Rd_Data #(wd_id, wd_data, wd_user)) - axi4_syncbuf <- mkAXI_SyncBuffer (depth, clk1, rst1, clk2, rst2); + AXIx_SyncBuffer_IFC #(AXI4_AW #(wd_id, wd_addr, wd_user), + AXI4_W #(wd_data, wd_user), + AXI4_B #(wd_id, wd_user), + AXI4_AR #(wd_id, wd_addr, wd_user), + AXI4_R #(wd_id, wd_data, wd_user)) - // Transactor with ifc2 - AXI4_M_Xactor_IFC #(wd_id, wd_addr, wd_data, wd_user) - axi4_M_xactor <- mkAXI4_M_Xactor (clocked_by clk2, reset_by rst2); + axi4_syncbuf <- mkAXIx_SyncBuffer (depth, clk1, rst1, clk2, rst2); // ---------------- - mkConnection (axi4_M_1, axi4_S_xactor.axi_side); - - mkConnection (axi4_S_xactor.o_wr_addr, axi4_syncbuf.from_M.i_aw); - mkConnection (axi4_S_xactor.o_wr_data, axi4_syncbuf.from_M.i_w); - mkConnection (axi4_S_xactor.i_wr_resp, axi4_syncbuf.from_M.o_b); - mkConnection (axi4_S_xactor.o_rd_addr, axi4_syncbuf.from_M.i_ar); - mkConnection (axi4_S_xactor.i_rd_data, axi4_syncbuf.from_M.o_r); - - mkConnection (axi4_syncbuf.to_S.o_aw, axi4_M_xactor.i_wr_addr); - mkConnection (axi4_syncbuf.to_S.o_w, axi4_M_xactor.i_wr_data); - mkConnection (axi4_syncbuf.to_S.i_b, axi4_M_xactor.o_wr_resp); - mkConnection (axi4_syncbuf.to_S.o_ar, axi4_M_xactor.i_rd_addr); - mkConnection (axi4_syncbuf.to_S.i_r, axi4_M_xactor.o_rd_data); + mkConnection (ifc_M.o_AW, axi4_syncbuf.from_M.i_aw); + mkConnection (ifc_M.o_W, axi4_syncbuf.from_M.i_w); + mkConnection (ifc_M.i_B, axi4_syncbuf.from_M.o_b); + mkConnection (ifc_M.o_AR, axi4_syncbuf.from_M.i_ar); + mkConnection (ifc_M.i_R, axi4_syncbuf.from_M.o_r); // ---------------- // INTERFACE - let axi4_M_2 = axi4_M_xactor.axi_side; - return axi4_M_2; + interface o_AW = axi4_syncbuf.to_S.o_aw; + interface o_W = axi4_syncbuf.to_S.o_w; + interface i_B = axi4_syncbuf.to_S.i_b; + interface o_AR = axi4_syncbuf.to_S.o_ar; + interface i_R = axi4_syncbuf.to_S.i_r; endmodule // ================================================================ +// Standalone clock-crosser with M and S interfaces // Clock1 -> Clock2 -> Module #(AXI4_Clock_Crossing_IFC) -// ---------------- - -interface AXI4_ClockCrossing_IFC #(numeric type id_, - numeric type addr_, - numeric type data_, - numeric type user_); - interface AXI4_S_IFC #(id_, addr_, data_, user_) from_M; +interface AXI4_SyncBuffer_IFC #(numeric type id_, + numeric type addr_, + numeric type data_, + numeric type user_); + interface AXI4_S_IFC #(id_, addr_, data_, user_) from_M; interface AXI4_M_IFC #(id_, addr_, data_, user_) to_S; endinterface // ---------------- -module mkAXI4_ClockCrossing - #(Clock clock_M, Reset reset_M, - Clock clock_S, Reset reset_S) - (AXI4_ClockCrossing_IFC #(id_, addr_, data_, user_)); - - // SyncFIFOs for the 5 channels - - SyncFIFOIfc #(AXI4_Wr_Addr #(id_, addr_, user_)) - f_aw <- mkSyncFIFO (4, clock_M, reset_M, clock_S); - - SyncFIFOIfc #(AXI4_Wr_Data #(data_, user_)) - f_w <- mkSyncFIFO (4, clock_M, reset_M, clock_S); - - SyncFIFOIfc #(AXI4_Wr_Resp #(id_, user_)) - f_b <- mkSyncFIFO (4, clock_S, reset_S, clock_M); - - SyncFIFOIfc #(AXI4_Rd_Addr #(id_, addr_, user_)) - f_ar <- mkSyncFIFO (4, clock_M, reset_M, clock_S); +module mkAXI4_SyncBuffer #(Integer depth, + Clock clock_M, Reset reset_M, + Clock clock_S, Reset reset_S) + (AXI4_SyncBuffer_IFC #(id_, addr_, data_, user_)); - SyncFIFOIfc #(AXI4_Rd_Data #(id_, data_, user_)) - f_r <- mkSyncFIFO (4, clock_S, reset_S, clock_M); - - // Transactors for each end - - AXI4_S_IFC #(id_, addr_, data_, user_) - xactor_S <- mkAXI4_Xactor_S_3 (f_aw, f_w, f_b, f_ar, f_r, - clocked_by clock_M, - reset_by reset_M); - AXI4_M_IFC #(id_, addr_, data_, user_) - xactor_M <- mkAXI4_Xactor_M_3 (f_aw, f_w, f_b, f_ar, f_r, - clocked_by clock_S, - reset_by reset_S); + let axi4_syncbuf <- mkAXIx_SyncBuffer (depth, clock_M, reset_M, clock_S, reset_S); // ---------------- - interface AXI4_S_IFC from_M = xactor_S; - interface AXI4_M_IFC to_S = xactor_M; + // INTERFACE + + interface AXI4_S_IFC from_M; + interface i_AW = axi4_syncbuf.from_M.i_aw; + interface i_W = axi4_syncbuf.from_M.i_w; + interface o_B = axi4_syncbuf.from_M.o_b; + interface i_AR = axi4_syncbuf.from_M.i_ar; + interface o_R = axi4_syncbuf.from_M.o_r; + endinterface + interface AXI4_M_IFC to_S; + interface o_AW = axi4_syncbuf.to_S.o_aw; + interface o_W = axi4_syncbuf.to_S.o_w; + interface i_B = axi4_syncbuf.to_S.i_b; + interface o_AR = axi4_syncbuf.to_S.o_ar; + interface i_R = axi4_syncbuf.to_S.i_r; + endinterface endmodule // ---------------------------------------------------------------- // Same as above, with S-side using current-clock -module mkAXI4_ClockCrossingToCC - #(Clock clock_M, Reset reset_M) - (AXI4_ClockCrossing_IFC #(id_, addr_, data_, user_)); +module mkAXI4_SyncBufferToCC #(Integer depth, + Clock clock_M, Reset reset_M) + (AXI4_SyncBuffer_IFC #(id_, addr_, data_, user_)); let clock_S <- exposeCurrentClock; let reset_S <- exposeCurrentReset; - let crossing <- mkAXI4_ClockCrossing (clock_M, reset_M, - clock_S, reset_S); - + let crossing <- mkAXI4_SyncBuffer (depth, + clock_M, reset_M, + clock_S, reset_S); return crossing; endmodule diff --git a/Libraries/AMBA_Fabrics/AXI4/AXI4_DDR_Model.bsv b/Libraries/AMBA_Fabrics/AXI4/AXI4_DDR_Model.bsv deleted file mode 100644 index 842c9c7..0000000 --- a/Libraries/AMBA_Fabrics/AXI4/AXI4_DDR_Model.bsv +++ /dev/null @@ -1,243 +0,0 @@ -// Copyright (c) 2016-2024 Bluespec, Inc. All Rights Reserved. -// -// SPDX-License-Identifier: BSD-3-Clause - -// A simple memory-model, to be used in simulation, with an AXI4 interface. -// Can optionally pre-load data with a memhex file. -// Author: Rishiyur S. Nikhil - -package AXI4_DDR_Model; - -// ================================================================ -// BSV lib imports - -import Vector :: *; -import RegFile :: *; -import Connectable :: *; - -// ---------------- -// BSV additional libs - -import Semi_FIFOF :: *; -import Cur_Cycle :: *; - -// ================================================================ -// Project imports - -import AXI4_Types :: *; -import AXI4_Deburster :: *; - -// ================================================================ - -export mkDDR_A_Model; -export AXI4_16_64_512_0_S_IFC; - -// ================================================================ -// DDR base and limit addresses - -Bit #(64) ddr_A_base = 'h_0_0000_0000; -Bit #(64) ddr_A_lim = 'h_0_8000_0000; - -// ================================================================ - -typedef AXI4_S_IFC #(16, // Id - 64, // Addr - 512, // Data - 0) // User - AXI4_16_64_512_0_S_IFC; - -typedef AXI4_S_Xactor_IFC #(16, // Id - 64, // Addr - 512, // Data - 0) // User - AXI4_16_64_512_0_S_Xactor_IFC; - -// ================================================================ - -function Bit #(512) fv_new_data (Bit #(512) old_data, Bit #(512) new_data, Bit #(64) strb); - function Bit #(8) f (Integer j); - return ((strb [j] == 1'b1) ? 'hFF : 'h00); - endfunction - Vector #(64, Bit #(8)) v_mask = genWith (f); - Bit #(512) mask = pack (v_mask); - return ((old_data & (~ mask)) | (new_data & mask)); -endfunction - -// ================================================================ -// DDR_A -// Supports bursts - -(* synthesize *) -module mkDDR_A_Model (AXI4_16_64_512_0_S_IFC); - let ifc <- mkMem_Model (0, // verbosity - 0, // ddr_num - False, // init_with_memhex - "DDR_A.memhex512", // memhex_filename - ddr_A_base, // byte_addr_base - ddr_A_lim, // byte_addr_lim - 'h_1_0000_0000); // bytes_implemented (4 GB) - AXI4_Deburster_IFC #(16, 64, 512, 0) deburster <- mkAXI4_Deburster; - mkConnection (deburster.to_S, ifc); - return deburster.from_M; -endmodule - -// ================================================================ -// Common implementation -// - ddr_num is unique id for each DDR (A=0, B=1, C=2, D=3) -// - init_with_memhex and memhex_file are for optional initialization from a memhex512 -// - byte_addr_base and byte_addr_lim are the range of byte-addrs served by this DDR -// (in AWS: 16GB) -// - bytes_implemented are the # of bytes implemented (on top of byte_addr_base) -// which need not cover until addr_last. -// WARNING: This is a simplified model: does not support AXI4 bursts. -// Use a deburster in front of this, if needed. - -module mkMem_Model #(Integer verbosity, - Bit #(2) ddr_num, - Bool init_with_memhex, - String memhex_filename, - Bit #(64) byte_addr_base, - Bit #(64) byte_addr_lim, - Bit #(64) bytes_implemented_param) - (AXI4_16_64_512_0_S_IFC); - - // Note: each 'word' in the RegFile is 512b = 64B => uses 6 lsbs of address. - Bit #(64) bytes_implemented = min (bytes_implemented_param, byte_addr_lim - byte_addr_base); - Bit #(64) words_implemented = (bytes_implemented >> 6); - Bit #(64) addr_align_mask = (~ 'h3F); - - RegFile #(Bit #(64), Bit #(512)) rf <- (init_with_memhex - ? mkRegFileLoad (memhex_filename, - 0, - words_implemented - 1) - : mkRegFile (0, words_implemented - 1)); - - AXI4_16_64_512_0_S_Xactor_IFC axi4_xactor <- mkAXI4_S_Xactor; - - Bit #(64) implem_addr_lim = byte_addr_base + (bytes_implemented & addr_align_mask); - - // ================================================================ - // BEHAVIOR - - // ---------------- - // For debugging only - - Reg #(Bool) rg_display_info <- mkReg (False); // To get debugging info => True - - rule rl_info (rg_display_info); - rg_display_info <= False; - - $display ("INFO: %m"); - $display (" base 0x%16h lim 0x%16h implemented 0x%16h", - byte_addr_base, byte_addr_lim, bytes_implemented); - if (init_with_memhex) - $display (" initialized from: %s", memhex_filename); - endrule - - // ---------------- - // Read requests - - rule rl_rd_req; - let rda <- pop_o (axi4_xactor.o_rd_addr); - - Bool ok1 = ((byte_addr_base <= rda.araddr) && (rda.araddr < byte_addr_lim)); - Bool ok2 = (rda.araddr < implem_addr_lim); - let offset_b = rda.araddr - byte_addr_base; - let offset_W = (offset_b >> 6); - - // Default error response - let rdd = AXI4_Rd_Data {rid: rda.arid, - rdata: zeroExtend (rda.araddr), // To help debugging - rresp: axi4_resp_slverr, - rlast: True, - ruser: ?}; - - if (! ok1) begin - $display ("%0d: Mem_Model [%0d]: rl_rd_req: addr %0h -> OUT OF BOUNDS", - cur_cycle, ddr_num, rda.araddr); - $display (" base %016h lim %016h", byte_addr_base, byte_addr_lim); - end - else if (! ok2) begin - $display ("%0d: Mem_Model [%0d]: rl_rd_req: addr %0h -> OUT OF IMPLEMENTED BOUNDS", - cur_cycle, ddr_num, rda.araddr); - $display (" base %016h implementation lim %016h", - byte_addr_base, implem_addr_lim); - end - else begin - let data = rf.sub (offset_W); - rdd = AXI4_Rd_Data {rid: rda.arid, - rdata: data, - rresp: axi4_resp_okay, - rlast: True, - ruser: ?}; - if (verbosity > 0) begin - $display ("%0d: Mem_Model [%0d]: rl_rd_req: addr %0h", - cur_cycle, ddr_num, rda.araddr); - $display (" data_hi %064h", data [511:256]); - $display (" data_lo %064h", data [255:0]); - end - end - - axi4_xactor.i_rd_data.enq (rdd); - endrule - - // ---------------- - // Write requests - - rule rl_wr_req; - let wra <- pop_o (axi4_xactor.o_wr_addr); - let wrd <- pop_o (axi4_xactor.o_wr_data); - - Bool ok1 = ((byte_addr_base <= wra.awaddr) && (wra.awaddr < byte_addr_lim)); - Bool ok2 = (wra.awaddr < implem_addr_lim); - let offset_b = wra.awaddr - byte_addr_base; - let offset_W = (offset_b >> 6); - - // Default error response - let wrr = AXI4_Wr_Resp {bid: wra.awid, bresp: axi4_resp_slverr, buser: ?}; - - if (! ok1) begin - $display ("%0d: Mem_Model [%0d]: rl_wr_req: OUT OF BOUNDS", - cur_cycle, ddr_num); - $display (" addr %0h <= %0h strb %0h", - wra.awaddr, wrd.wdata, wrd.wstrb); - $display (" base %016h lim %016h", byte_addr_base, byte_addr_lim); - end - else if (! ok2) begin - $display ("%0d: Mem_Model [%0d]: rl_wr_req: OUT OF IMPLEMENTED BOUNDS", - cur_cycle, ddr_num); - $display (" addr %0h <= %0h strb %0h", - wra.awaddr, wrd.wdata, wrd.wstrb); - $display (" base %016h implementation lim %016h", - byte_addr_base, implem_addr_lim); - end - else begin - let old_data = rf.sub (offset_W); - let new_data = fv_new_data (old_data, wrd.wdata, wrd.wstrb); - rf.upd (offset_W, new_data); - if (verbosity > 1) begin - $display (" Old: %h", old_data); - $display (" New: %h", new_data); - end - wrr = AXI4_Wr_Resp {bid: wra.awid, bresp: axi4_resp_okay, buser: ?}; - - if (verbosity > 0) begin - $display ("%0d: Mem_Model [%0d]: rl_wr_req: addr %0h strb %0h", - cur_cycle, ddr_num, wra.awaddr, wrd.wstrb); - $display (" data_hi %064h", wrd.wdata [511:256]); - $display (" data_lo %064h", wrd.wdata [255:0]); - end - end - - axi4_xactor.i_wr_resp.enq (wrr); - endrule - - // ================================================================ - // INTERFACE - - return axi4_xactor.axi_side; -endmodule - -// ================================================================ - -endpackage diff --git a/Libraries/AMBA_Fabrics/AXI4/AXI4_Deburster.bsv b/Libraries/AMBA_Fabrics/AXI4/AXI4_Deburster.bsv index effdd5c..4bef2cc 100644 --- a/Libraries/AMBA_Fabrics/AXI4/AXI4_Deburster.bsv +++ b/Libraries/AMBA_Fabrics/AXI4/AXI4_Deburster.bsv @@ -1,13 +1,17 @@ -// Copyright (c) 2019 Bluespec, Inc. All Rights Reserved -// +// Copyright (c) 2019-2023 Bluespec, Inc. All Rights Reserved +// Copyright (c) 2024 Rishiyur S. Nikhil. + // SPDX-License-Identifier: BSD-3-Clause package AXI4_Deburster; // ================================================================ // This package defines a AXI4-S-to-AXI4-S conversion module. -// The M-side interface is an AXI4-S that carries no burst transactions. -// The S-side interface is an AXI4-S that carries burst transactions. +// The upstream interface (AXI4-S) is an AXI4-S that carries burst transactions. +// The module argument is the downstream interface, also an AXI4-S, +// which does not have bursts. +// i.e., a multi-beat burst request from upstream is sent to the +// downstream as a series of 1-beat requests. // ================================================================ // Bluespec library imports @@ -18,125 +22,77 @@ import SpecialFIFOs :: *; import ConfigReg :: *; // ---------------- -// BSV additional libs +// Bluespec misc. libs import Cur_Cycle :: *; - -// ================================================================ -// Project imports - import Semi_FIFOF :: *; -import AXI4_Types :: *; -// ================================================================ -// The interface for the fabric module +// ---------------- +// Project imports -interface AXI4_Deburster_IFC #(numeric type wd_id, - numeric type wd_addr, - numeric type wd_data, - numeric type wd_user); - method Action reset; +import AXI4_Types :: *; - // From M - interface AXI4_S_IFC #(wd_id, wd_addr, wd_data, wd_user) from_M; +// **************************************************************** +// Verbosity during simulation on stdout (edit this as desired): +// 0: quiet +// 1: display start of burst +// 2: display detail - // To S - interface AXI4_M_IFC #(wd_id, wd_addr, wd_data, wd_user) to_S; -endinterface +Integer verbosity = 0; -// ================================================================ +// **************************************************************** // The Deburster module -module mkAXI4_Deburster (AXI4_Deburster_IFC #(wd_id, wd_addr, wd_data, wd_user)) +module mkAXI4_Deburster #(AXI4_S_IFC #(wd_id, wd_addr, wd_data, wd_user) ifc_S) + (AXI4_S_IFC #(wd_id, wd_addr, wd_data, wd_user)) provisos (Add #(a__, 8, wd_addr)); - // 0 quiet; 1: display start of burst; 2: display all traffic - Integer verbosity = 0; + // Buffer facing M + AXI4_Buffer_IFC #(wd_id, wd_addr, wd_data, wd_user) xactor_from_M <- mkAXI4_Buffer; - Reg #(Bool) rg_reset <- mkReg (True); - - // Transactor facing M - AXI4_S_Xactor_IFC #(wd_id, wd_addr, wd_data, wd_user) - xaxtor_from_M <- mkAXI4_S_Xactor; - - // Transactor facing S - AXI4_M_Xactor_IFC #(wd_id, wd_addr, wd_data, wd_user) - xactor_to_S <- mkAXI4_M_Xactor; + // ---------------- + // Write-transaction book-keeping - // On a write-transaction, this register is the W-channel burst beat count - // (0 => start of burst) + // This reg is W-channel burst beat count (0 => start of burst) Reg #(AXI4_Len) rg_w_beat_count <- mkReg (0); - // On a write-transaction, records awlen for S - // Size of FIFO should cover S latency + // Records awlen. + // Size of FIFO should cover S latency (because used on B to + // combine B responses). FIFOF #(AXI4_Len) f_w_awlen <- mkSizedFIFOF (4); - // On a write-transaction, this register is the B-channel burst - // beat count which is the number of individual (non-burst) - // responses from the S to be combined into a single burst response - // to M. - // (0 => ready for next burst) + // This reg is the B-channel burst beat count which is the number + // of individual (non-burst) responses from the S to be combined + // into a single burst response to M. (0 => ready for next burst) Reg #(AXI4_Len) rg_b_beat_count <- mkReg (0); - // On a burst write-transaction, all the individual S responses may - // not have the same 'resp' on the B channel. This register - // remembers the first 'non-okay' resp (if any), to be returned to - // M in the burst response. + // All individual S responses may not have the same 'resp' on the B + // channel. This reg remembers first 'non-okay' resp (if any), to + // be returned to M in the burst response. Reg #(AXI4_Resp) rg_b_resp <- mkReg (axi4_resp_okay); - // On a read-transaction, records arlen for S - // Size of FIFO should cover S latency + // ---------------- + // Read-transaction book-keeping + + // Records arlen for S. + // Size of FIFO should cover S latency (because used on R to + // combine R-response into a burst). FIFOF #(AXI4_Len) f_r_arlen <- mkSizedFIFOF (4); - // On a read-transaction, this register is the AR-channel burst beat count - // (0 => start of next burst) + // This reg is the AR-channel burst beat count (0 => start of next burst) Reg #(AXI4_Len) rg_ar_beat_count <- mkReg (0); - // On a read-transaction, this register is the R-channel burst beat count - // (0 => ready for next burst) + // This reg is the R-channel burst beat count (0 => ready for next burst) Reg #(AXI4_Len) rg_r_beat_count <- mkReg (0); // ---------------------------------------------------------------- - // Compute address for beat - -// function ActionValue#(Bit #(wd_addr)) fv_addr_for_beat (Bit #(wd_addr) start_addr, -// AXI4_Size axsize, -// AXI4_Burst axburst, -// AXI4_Len axlen, -// AXI4_Len beat_count); -// -// actionvalue -// // For incrementing bursts this address is the next address -// Bit #(wd_addr) addr = start_addr; -// addr = start_addr + (1 << pack (axsize)); -// -// // The actual length of the burst is one more than indicated by axlen -// Bit #(wd_addr) burst_len = zeroExtend (axlen) + 1; -// -// // find the wrap boundary bit - this becomes the mask - will only work -// // for burst lengths which are a power of two -// Bit #(wd_addr) wrap_boundary = (burst_len << pack (axsize)); -// -// // For wrapping bursts the wrap_mask needs to be applied to check if the -// // wrapping boundary has been reached -// if (axburst == axburst_wrap) begin -// $display ("%0d: %m::AXI4_Deburster: wrapping burst. boundary: (%0x). addr: (%0x)", cur_cycle, wrap_boundary, addr); -// // The wrapping condition -// if ((addr % wrap_boundary) == 0) begin -// // wrap the address - retain all bits except the wrap boundary bit -// addr = addr & (~wrap_boundary); -// $display ("%0d: %m::AXI4_Deburster: wrapping burst. Wrapping: addr: (%0x)", cur_cycle, addr); -// end -// end -// return addr; -// endactionvalue -// endfunction - - function Bit #(wd_addr) fv_addr_for_beat (Bit #(wd_addr) start_addr, - AXI4_Size axsize, - AXI4_Burst axburst, - AXI4_Len axlen, - AXI4_Len beat_count); + // Compute axaddr for beat + + function Bit #(wd_addr) fv_axaddr_for_beat (Bit #(wd_addr) start_addr, + AXI4_Size axsize, + AXI4_Burst axburst, + AXI4_Len axlen, + AXI4_Len beat_count); // For incrementing bursts this address is the next address Bit #(wd_addr) addr = start_addr; @@ -157,109 +113,92 @@ module mkAXI4_Deburster (AXI4_Deburster_IFC #(wd_id, wd_addr, wd_data, wd_user)) return addr; endfunction - // ---------------------------------------------------------------- - // RESET - - rule rl_reset (rg_reset); - if (verbosity >= 1) - $display ("%0d: %m::AXI4_Deburster.rl_reset", cur_cycle); - xaxtor_from_M.reset; - xactor_to_S.reset; - - f_w_awlen.clear; - rg_w_beat_count <= 0; - rg_b_beat_count <= 0; - rg_b_resp <= axi4_resp_okay; - - f_r_arlen.clear; - rg_ar_beat_count <= 0; - rg_r_beat_count <= 0; - - rg_reset <= False; - endrule - - // ---------------------------------------------------------------- + // ================================================================ // BEHAVIOR - Reg #(Bit #(wd_addr)) rg_last_beat_waddr <- mkRegU; // ---------------- - // Wr requests (AW and W channels) + // AW and W channels (write requests) + + Reg #(Bit #(wd_addr)) rg_last_beat_waddr <- mkRegU; - rule rl_wr_xaction_M_to_S; - AXI4_Wr_Addr #(wd_id, wd_addr, wd_user) a_in = xaxtor_from_M.o_wr_addr.first; - AXI4_Wr_Data #(wd_data, wd_user) d_in = xaxtor_from_M.o_wr_data.first; + rule rl_AW_W; + AXI4_AW #(wd_id, wd_addr, wd_user) aw_in = xactor_from_M.ifc_M.o_AW.first; + AXI4_W #(wd_data, wd_user) w_in = xactor_from_M.ifc_M.o_W.first; // Construct output AW item - let a_out = a_in; - // For the first beat the address is unchanged from the address in the - // input request, for the remaining beats we have the update the address + let aw_out = aw_in; + // For the first beat the address is unchanged from the address + // in the input request, for the remaining beats the address is // based on the previous address used - if (rg_w_beat_count != 0) begin - a_out.awaddr = fv_addr_for_beat (rg_last_beat_waddr, a_in.awsize, a_in.awburst, a_in.awlen, rg_w_beat_count); - end + if (rg_w_beat_count != 0) + aw_out.awaddr = fv_axaddr_for_beat (rg_last_beat_waddr, + aw_in.awsize, + aw_in.awburst, + aw_in.awlen, + rg_w_beat_count); - a_out.awlen = 0; - a_out.awburst = axburst_fixed; // Not necessary when awlen=1, but S may be finicky + aw_out.awlen = 0; + aw_out.awburst = axburst_fixed; // Not necessary when awlen=1, but S may be finicky // Set WLAST to true since this is always last beat of outgoing xaction (awlen=1) - let d_out = d_in; - d_out.wlast = True; + let w_out = w_in; + w_out.wlast = True; // Send to S - xactor_to_S.i_wr_addr.enq (a_out); - xactor_to_S.i_wr_data.enq (d_out); + ifc_S.i_AW.enq (aw_out); + ifc_S.i_W.enq (w_out); - xaxtor_from_M.o_wr_data.deq; + xactor_from_M.ifc_M.o_W.deq; // Remember burst length so that individual responses from S can // be combined into a single burst response to M. if (rg_w_beat_count == 0) - f_w_awlen.enq (a_in.awlen); + f_w_awlen.enq (aw_in.awlen); - if (rg_w_beat_count < a_in.awlen) begin + if (rg_w_beat_count < aw_in.awlen) begin rg_w_beat_count <= rg_w_beat_count + 1; end else begin // Last beat of incoming burst; done with AW item - xaxtor_from_M.o_wr_addr.deq; + xactor_from_M.ifc_M.o_AW.deq; rg_w_beat_count <= 0; // Simulation-only assertion-check (no action, just display assertion failure) // Last incoming beat must have WLAST = 1 - if (! d_in.wlast) begin - $display ("%0d: ERROR: %m::AXI4_Deburster.rl_wr_xaction_M_to_S: m -> s", + if (! w_in.wlast) begin + $display ("%0d: ERROR: AXI4_Deburster.rl_AW_W: m -> s", cur_cycle); - $display (" WLAST not set on last data beat (awlen = %0d)", a_in.awlen); - $display (" ", fshow (d_in)); + $display (" WLAST not set on last data beat (awlen = %0d)", aw_in.awlen); + $display (" ", fshow (w_in)); end end // Remember this beat's address for calculating the next beat address. // This is necessary to support wrapping bursts - rg_last_beat_waddr <= a_out.awaddr; + rg_last_beat_waddr <= aw_out.awaddr; // Debugging if (verbosity > 0) begin - $display ("%0d: %m::AXI4_Deburster.rl_wr_xaction_M_to_S: m -> s, beat %0d", + $display ("%0d: AXI4_Deburster.rl_AW_W: m -> s, beat %0d", cur_cycle, rg_w_beat_count); if (rg_w_beat_count == 0) - $display (" a_in : ", fshow (a_in)); + $display (" aw_in : ", fshow (aw_in)); if ((rg_w_beat_count == 0) || (verbosity > 1)) begin - $display (" d_in : ", fshow (d_in)); - $display (" a_out: ", fshow (a_out)); - $display (" d_out: ", fshow (d_out)); + $display (" w_in : ", fshow (w_in)); + $display (" aw_out: ", fshow (aw_out)); + $display (" w_out: ", fshow (w_out)); end end - endrule: rl_wr_xaction_M_to_S + endrule: rl_AW_W // ---------------- - // Wr responses (B channel): consume responses from until the last - // response for a burst, then respond to M. Remember if any of - // them was not an 'okay' response. + // B channel (write responses): consume responses from until the + // last response for a burst, then respond to M. Remember if any + // of them was not an 'okay' response. - rule rl_wr_resp_S_to_M; - AXI4_Wr_Resp #(wd_id, wd_user) b_in <- pop_o (xactor_to_S.o_wr_resp); + rule rl_B_S_to_M; + AXI4_B #(wd_id, wd_user) b_in <- pop_o (ifc_S.o_B); if (rg_b_beat_count < f_w_awlen.first) begin // Remember first non-okay response (if any) of a burst in rg_b_resp @@ -270,7 +209,7 @@ module mkAXI4_Deburster (AXI4_Deburster_IFC #(wd_id, wd_addr, wd_data, wd_user)) rg_b_beat_count <= rg_b_beat_count + 1; if (verbosity > 1) begin - $display ("%0d: %m::AXI4_Deburster.rl_wr_resp_S_to_M: m <- s, beat %0d", + $display ("%0d: AXI4_Deburster.rl_B_S_to_M: m <- s, beat %0d", cur_cycle, rg_b_beat_count); $display (" Consuming and discarding beat %0d", rg_b_beat_count); $display (" ", fshow (b_in)); @@ -281,7 +220,7 @@ module mkAXI4_Deburster (AXI4_Deburster_IFC #(wd_id, wd_addr, wd_data, wd_user)) let b_out = b_in; if (rg_b_resp != axi4_resp_okay) b_out.bresp = rg_b_resp; - xaxtor_from_M.i_wr_resp.enq (b_out); + xactor_from_M.ifc_M.i_B.enq (b_out); f_w_awlen.deq; @@ -290,7 +229,7 @@ module mkAXI4_Deburster (AXI4_Deburster_IFC #(wd_id, wd_addr, wd_data, wd_user)) rg_b_resp <= axi4_resp_okay; if (verbosity > 1) begin - $display ("%0d: %m::AXI4_Deburster.rl_wr_resp_S_to_M: m <- s, beat %0d", + $display ("%0d: AXI4_Deburster.rl_B_S_to_M: m <- s, beat %0d", cur_cycle, rg_b_beat_count); $display (" b_in: ", fshow (b_in)); $display (" b_out: ", fshow (b_out)); @@ -298,56 +237,56 @@ module mkAXI4_Deburster (AXI4_Deburster_IFC #(wd_id, wd_addr, wd_data, wd_user)) end endrule - // ---------------- - // Rd requests (AR channel) + // ---------------- + // AR channel (read requests) Reg #(Bit #(wd_addr)) rg_last_beat_raddr <- mkRegU; rule rl_rd_xaction_M_to_S; - AXI4_Rd_Addr #(wd_id, wd_addr, wd_user) a_in = xaxtor_from_M.o_rd_addr.first; + AXI4_AR #(wd_id, wd_addr, wd_user) ar_in = xactor_from_M.ifc_M.o_AR.first; // Compute forwarded request for each beat, and send - let a_out = a_in; + let ar_out = ar_in; // For the first beat the address is unchanged from the address in the // input request, for the remaining beats we have the update the address // based on the previous address used if (rg_ar_beat_count != 0) begin - a_out.araddr = fv_addr_for_beat (rg_last_beat_raddr, - a_in.arsize, - a_in.arburst, - a_in.arlen, - rg_ar_beat_count); + ar_out.araddr = fv_axaddr_for_beat (rg_last_beat_raddr, + ar_in.arsize, + ar_in.arburst, + ar_in.arlen, + rg_ar_beat_count); end - a_out.arlen = 0; - a_out.arburst = axburst_fixed; // Not necessary when arlen=1, but S may be finicky - xactor_to_S.i_rd_addr.enq (a_out); + ar_out.arlen = 0; + ar_out.arburst = axburst_fixed; // Not necessary when arlen=1, but S may be finicky + ifc_S.i_AR.enq (ar_out); // On first beat, set up the response count if (rg_ar_beat_count == 0) - f_r_arlen.enq (a_in.arlen); + f_r_arlen.enq (ar_in.arlen); - if (rg_ar_beat_count < a_in.arlen) begin + if (rg_ar_beat_count < ar_in.arlen) begin rg_ar_beat_count <= rg_ar_beat_count + 1; end else begin // Last beat sent; done with AR item - xaxtor_from_M.o_rd_addr.deq; + xactor_from_M.ifc_M.o_AR.deq; rg_ar_beat_count <= 0; end // Remember this beat's address for calculating the next beat address. // This is necessary to support wrapping bursts - rg_last_beat_raddr <= a_out.araddr; + rg_last_beat_raddr <= ar_out.araddr; // Debugging if (verbosity > 0) begin - $display ("%0d: %m::AXI4_Deburster.rl_rd_xaction_M_to_S: m -> s, addr %08x beat %0d", - cur_cycle, a_out.araddr, rg_ar_beat_count); + $display ("%0d: AXI4_Deburster.rl_rd_xaction_M_to_S: m -> s, addr %08x beat %0d", + cur_cycle, ar_out.araddr, rg_ar_beat_count); if (rg_ar_beat_count == 0) - $display (" a_in: ", fshow (a_in)); + $display (" ar_in: ", fshow (ar_in)); if ((rg_ar_beat_count == 0) || (verbosity > 1)) - $display (" a_out: ", fshow (a_out)); + $display (" ar_out: ", fshow (ar_out)); end endrule: rl_rd_xaction_M_to_S @@ -356,7 +295,7 @@ module mkAXI4_Deburster (AXI4_Deburster_IFC #(wd_id, wd_addr, wd_data, wd_user)) // Rd responses rule rl_rd_resp_S_to_M; - AXI4_Rd_Data #(wd_id, wd_data, wd_user) r_in <- pop_o (xactor_to_S.o_rd_data); + AXI4_R #(wd_id, wd_data, wd_user) r_in <- pop_o (ifc_S.o_R); let arlen = f_r_arlen.first; let r_out = r_in; @@ -372,11 +311,11 @@ module mkAXI4_Deburster (AXI4_Deburster_IFC #(wd_id, wd_addr, wd_data, wd_user)) f_r_arlen.deq; end - xaxtor_from_M.i_rd_data.enq (r_out); + xactor_from_M.ifc_M.i_R.enq (r_out); // Debugging if (verbosity > 0) begin - $display ("%0d: %m::AXI4_Deburster.rl_rd_resp_S_to_M: m <- s, beat %0d", + $display ("%0d: AXI4_Deburster.rl_rd_resp_S_to_M: m <- s, beat %0d", cur_cycle, rg_r_beat_count); if ((rg_r_beat_count == 0) || (verbosity > 1)) begin $display (" r_in: ", fshow (r_in)); @@ -385,17 +324,12 @@ module mkAXI4_Deburster (AXI4_Deburster_IFC #(wd_id, wd_addr, wd_data, wd_user)) end endrule: rl_rd_resp_S_to_M - // ---------------------------------------------------------------- + // **************************************************************** // INTERFACE - method Action reset () if (! rg_reset); - rg_reset <= True; - endmethod - - interface from_M = xaxtor_from_M.axi_side; - interface to_S = xactor_to_S .axi_side; + return xactor_from_M.ifc_S; endmodule -// ================================================================ +// **************************************************************** endpackage: AXI4_Deburster diff --git a/Libraries/AMBA_Fabrics/AXI4/AXI4_Extra_Xactors.bsv b/Libraries/AMBA_Fabrics/AXI4/AXI4_Extra_Xactors.bsv deleted file mode 100644 index 2d4e5f8..0000000 --- a/Libraries/AMBA_Fabrics/AXI4/AXI4_Extra_Xactors.bsv +++ /dev/null @@ -1,380 +0,0 @@ -// Copyright (c) 2019 Bluespec, Inc. All Rights Reserved -// -// SPDX-License-Identifier: BSD-3-Clause - -import FIFOF ::*; -import Semi_FIFOF::*; -import AXI4_Types::*; - -// This part based on code written in the University of Cambridge Computer Laboratory. - -typeclass ToUnguarded #(type a); - module mkUnguarded #(a x)(a); -endtypeclass - -instance ToUnguarded #(FIFOF_I #(a)) - provisos (Bits#(a, __)); - module mkUnguarded #(FIFOF_I #(a) ifc)(FIFOF_I #(a)); - let enqWire <- mkRWire; - rule warnDoEnq (isValid(enqWire.wget) && !ifc.notFull); - $display("WARNING: enqing into an already full FIFOF_I"); - $finish(0); - endrule - rule doEnq (isValid(enqWire.wget)); - ifc.enq(enqWire.wget.Valid); - endrule - return interface FIFOF_I; - method notFull = ifc.notFull; - method enq = enqWire.wset; - endinterface; - endmodule -endinstance - -instance ToUnguarded #(FIFOF_O #(a)) - provisos (Bits#(a, _)); - module mkUnguarded#(FIFOF_O #(a) ifc)(FIFOF_O#(a)); - let firstWire <- mkDWire(unpack(0)); - let deqWire <- mkPulseWire; - rule setFirst; firstWire <= ifc.first; endrule - rule warnDoDeq (deqWire && !ifc.notEmpty); - $display("WARNING: deqing from empty FIFOF_O"); - $finish(0); - endrule - rule doDeq (deqWire && ifc.notEmpty); - ifc.deq; - endrule - return interface FIFOF_O; - method notEmpty = ifc.notEmpty; - method first = firstWire; - method deq = deqWire.send; - endinterface; - endmodule -endinstance - -// -------------------------------------- - -module mkAXI4_M_Xactor_3 #(aw_t aw, w_t w, b_t b, ar_t ar, r_t r) - (AXI4_M_IFC #(wd_id, wd_addr, wd_data, wd_user)) - provisos (To_FIFOF_IO #(aw_t, AXI4_Wr_Addr #(wd_id, wd_addr, wd_user)), - To_FIFOF_IO #(w_t, AXI4_Wr_Data #(wd_data, wd_user)), - To_FIFOF_IO #(b_t, AXI4_Wr_Resp #(wd_id, wd_user)), - To_FIFOF_IO #(ar_t, AXI4_Rd_Addr #(wd_id, wd_addr, wd_user)), - To_FIFOF_IO #(r_t, AXI4_Rd_Data #(wd_id, wd_data, wd_user))); - - FIFOF_O #(AXI4_Wr_Addr #(wd_id, wd_addr, wd_user)) f_wr_addr <- mkUnguarded (to_FIFOF_O (aw)); - FIFOF_O #(AXI4_Wr_Data #(wd_data, wd_user)) f_wr_data <- mkUnguarded (to_FIFOF_O (w)); - FIFOF_I #(AXI4_Wr_Resp #(wd_id, wd_user)) f_wr_resp <- mkUnguarded (to_FIFOF_I (b)); - - FIFOF_O #(AXI4_Rd_Addr #(wd_id, wd_addr, wd_user)) f_rd_addr <- mkUnguarded (to_FIFOF_O (ar)); - FIFOF_I #(AXI4_Rd_Data #(wd_id, wd_data, wd_user)) f_rd_data <- mkUnguarded (to_FIFOF_I (r)); - - // ---------------------------------------------------------------- - // INTERFACE - - return interface AXI4_M_IFC; - // Wr Addr channel - method Bool m_awvalid = f_wr_addr.notEmpty; - method Bit #(wd_id) m_awid = f_wr_addr.first.awid; - method Bit #(wd_addr) m_awaddr = f_wr_addr.first.awaddr; - method Bit #(8) m_awlen = f_wr_addr.first.awlen; - method AXI4_Size m_awsize = f_wr_addr.first.awsize; - method Bit #(2) m_awburst = f_wr_addr.first.awburst; - method Bit #(1) m_awlock = f_wr_addr.first.awlock; - method Bit #(4) m_awcache = f_wr_addr.first.awcache; - method Bit #(3) m_awprot = f_wr_addr.first.awprot; - method Bit #(4) m_awqos = f_wr_addr.first.awqos; - method Bit #(4) m_awregion = f_wr_addr.first.awregion; - method Bit #(wd_user) m_awuser = f_wr_addr.first.awuser; - method Action m_awready (Bool awready); - if (f_wr_addr.notEmpty && awready) f_wr_addr.deq; - endmethod - - // Wr Data channel - method Bool m_wvalid = f_wr_data.notEmpty; - method Bit #(wd_data) m_wdata = f_wr_data.first.wdata; - method Bit #(TDiv #(wd_data, 8)) m_wstrb = f_wr_data.first.wstrb; - method Bool m_wlast = f_wr_data.first.wlast; - method Bit #(wd_user) m_wuser = f_wr_data.first.wuser; - method Action m_wready (Bool wready); - if (f_wr_data.notEmpty && wready) f_wr_data.deq; - endmethod - - // Wr Response channel - method Action m_bvalid (Bool bvalid, - Bit #(wd_id) bid, - Bit #(2) bresp, - Bit #(wd_user) buser); - if (bvalid && f_wr_resp.notFull) - f_wr_resp.enq (AXI4_Wr_Resp {bid: bid, - bresp: bresp, - buser: buser}); - endmethod - - method Bool m_bready; - return f_wr_resp.notFull; - endmethod - - // Rd Addr channel - method Bool m_arvalid = f_rd_addr.notEmpty; - method Bit #(wd_id) m_arid = f_rd_addr.first.arid; - method Bit #(wd_addr) m_araddr = f_rd_addr.first.araddr; - method Bit #(8) m_arlen = f_rd_addr.first.arlen; - method AXI4_Size m_arsize = f_rd_addr.first.arsize; - method Bit #(2) m_arburst = f_rd_addr.first.arburst; - method Bit #(1) m_arlock = f_rd_addr.first.arlock; - method Bit #(4) m_arcache = f_rd_addr.first.arcache; - method Bit #(3) m_arprot = f_rd_addr.first.arprot; - method Bit #(4) m_arqos = f_rd_addr.first.arqos; - method Bit #(4) m_arregion = f_rd_addr.first.arregion; - method Bit #(wd_user) m_aruser = f_rd_addr.first.aruser; - - method Action m_arready (Bool arready); - if (f_rd_addr.notEmpty && arready) f_rd_addr.deq; - endmethod - - // Rd Data channel - method Action m_rvalid (Bool rvalid, // in - Bit #(wd_id) rid, // in - Bit #(wd_data) rdata, // in - Bit #(2) rresp, // in - Bool rlast, // in - Bit #(wd_user) ruser); // in - if (rvalid && f_rd_data.notFull) - f_rd_data.enq (AXI4_Rd_Data {rid: rid, - rdata: rdata, - rresp: rresp, - rlast: rlast, - ruser: ruser}); - endmethod - - method Bool m_rready; - return f_rd_data.notFull; - endmethod - - endinterface; -endmodule: mkAXI4_M_Xactor_3 - -module mkAXI4_S_Xactor_3 #(aw_t aw, w_t w, b_t b, ar_t ar, r_t r) - (AXI4_S_IFC #(wd_id, wd_addr, wd_data, wd_user)) - provisos (To_FIFOF_IO #(aw_t, AXI4_Wr_Addr #(wd_id, wd_addr, wd_user)), - To_FIFOF_IO #(w_t, AXI4_Wr_Data #(wd_data, wd_user)), - To_FIFOF_IO #(b_t, AXI4_Wr_Resp #(wd_id, wd_user)), - To_FIFOF_IO #(ar_t, AXI4_Rd_Addr #(wd_id, wd_addr, wd_user)), - To_FIFOF_IO #(r_t, AXI4_Rd_Data #(wd_id, wd_data, wd_user))); - - FIFOF_I #(AXI4_Wr_Addr #(wd_id, wd_addr, wd_user)) f_wr_addr <- mkUnguarded (to_FIFOF_I (aw)); - FIFOF_I #(AXI4_Wr_Data #(wd_data, wd_user)) f_wr_data <- mkUnguarded (to_FIFOF_I (w)); - FIFOF_O #(AXI4_Wr_Resp #(wd_id, wd_user)) f_wr_resp <- mkUnguarded (to_FIFOF_O (b)); - - FIFOF_I #(AXI4_Rd_Addr #(wd_id, wd_addr, wd_user)) f_rd_addr <- mkUnguarded (to_FIFOF_I (ar)); - FIFOF_O #(AXI4_Rd_Data #(wd_id, wd_data, wd_user)) f_rd_data <- mkUnguarded (to_FIFOF_O (r)); - - // ---------------------------------------------------------------- - // INTERFACE - - return interface AXI4_S_IFC; - // Wr Addr channel - method Action m_awvalid (Bool awvalid, - Bit #(wd_id) awid, - Bit #(wd_addr) awaddr, - Bit #(8) awlen, - AXI4_Size awsize, - Bit #(2) awburst, - Bit #(1) awlock, - Bit #(4) awcache, - Bit #(3) awprot, - Bit #(4) awqos, - Bit #(4) awregion, - Bit #(wd_user) awuser); - if (awvalid && f_wr_addr.notFull) - f_wr_addr.enq (AXI4_Wr_Addr {awid: awid, - awaddr: awaddr, - awlen: awlen, - awsize: awsize, - awburst: awburst, - awlock: awlock, - awcache: awcache, - awprot: awprot, - awqos: awqos, - awregion: awregion, - awuser: awuser}); - endmethod - - method Bool m_awready; - return f_wr_addr.notFull; - endmethod - - // Wr Data channel - method Action m_wvalid (Bool wvalid, - Bit #(wd_data) wdata, - Bit #(TDiv #(wd_data, 8)) wstrb, - Bool wlast, - Bit #(wd_user) wuser); - if (wvalid && f_wr_data.notFull) - f_wr_data.enq (AXI4_Wr_Data {wdata: wdata, - wstrb: wstrb, - wlast: wlast, - wuser: wuser}); - endmethod - - method Bool m_wready; - return f_wr_data.notFull; - endmethod - - // Wr Response channel - method Bool m_bvalid = f_wr_resp.notEmpty; - method Bit #(wd_id) m_bid = f_wr_resp.first.bid; - method Bit #(2) m_bresp = f_wr_resp.first.bresp; - method Bit #(wd_user) m_buser = f_wr_resp.first.buser; - method Action m_bready (Bool bready); - if (bready && f_wr_resp.notEmpty) - f_wr_resp.deq; - endmethod - - // Rd Addr channel - method Action m_arvalid (Bool arvalid, - Bit #(wd_id) arid, - Bit #(wd_addr) araddr, - Bit #(8) arlen, - AXI4_Size arsize, - Bit #(2) arburst, - Bit #(1) arlock, - Bit #(4) arcache, - Bit #(3) arprot, - Bit #(4) arqos, - Bit #(4) arregion, - Bit #(wd_user) aruser); - if (arvalid && f_rd_addr.notFull) - f_rd_addr.enq (AXI4_Rd_Addr {arid: arid, - araddr: araddr, - arlen: arlen, - arsize: arsize, - arburst: arburst, - arlock: arlock, - arcache: arcache, - arprot: arprot, - arqos: arqos, - arregion: arregion, - aruser: aruser}); - endmethod - - method Bool m_arready; - return f_rd_addr.notFull; - endmethod - - // Rd Data channel - method Bool m_rvalid = f_rd_data.notEmpty; - method Bit #(wd_id) m_rid = f_rd_data.first.rid; - method Bit #(wd_data) m_rdata = f_rd_data.first.rdata; - method Bit #(2) m_rresp = f_rd_data.first.rresp; - method Bool m_rlast = f_rd_data.first.rlast; - method Bit #(wd_user) m_ruser = f_rd_data.first.ruser; - method Action m_rready (Bool rready); - if (rready && f_rd_data.notEmpty) - f_rd_data.deq; - endmethod - endinterface; -endmodule: mkAXI4_S_Xactor_3 - -// -------------------------------------- - -typedef union tagged { - AXI4_Rd_Addr #(wd_id, wd_addr, wd_user) Read; - AXI4_Wr_Addr #(wd_id, wd_addr, wd_user) Write; - } AXI4_RdWr_Addr #(numeric type wd_id, - numeric type wd_addr, - numeric type wd_user) -deriving (Bits, FShow); - -interface Addr_FIFOF_Pair #(numeric type wd_id, - numeric type wd_addr, - numeric type wd_user); - interface FIFOF #(AXI4_Rd_Addr #(wd_id, wd_addr, wd_user)) ff_read; - interface FIFOF #(AXI4_Wr_Addr #(wd_id, wd_addr, wd_user)) ff_write; -endinterface - -module mkAddr_FIFOF_Pair (Addr_FIFOF_Pair #(wd_id, wd_addr, wd_user)); - Bool unguarded = True; - Bool guarded = False; - - FIFOF #(AXI4_RdWr_Addr #(wd_id, wd_addr, wd_user)) ff <- mkGFIFOF (guarded, unguarded); - - interface FIFOF ff_read; - method notFull = ff.notFull; - method Action enq(x); - ff.enq(tagged Read x); - endmethod - - method notEmpty = (ff.notEmpty && (ff.first matches tagged Read .x ? True : False)); - method first () if (ff.notEmpty &&& ff.first matches tagged Read .x); - return x; - endmethod - - method Action deq () if (ff.notEmpty &&& ff.first matches tagged Read .x); - ff.deq; - endmethod - - method Action clear; - ff.clear; - endmethod - endinterface - - interface FIFOF ff_write; - method notFull = ff.notFull; - method Action enq(x); - ff.enq(tagged Write x); - endmethod - - method notEmpty = (ff.notEmpty && (ff.first matches tagged Write .x ? True : False)); - method first () if (ff.notEmpty &&& ff.first matches tagged Write .x); - return x; - endmethod - - method Action deq () if (ff.notEmpty &&& ff.first matches tagged Write .x); - ff.deq; - endmethod - - method Action clear; - ff.clear; - endmethod - endinterface -endmodule - -module mkAXI4_Serializing_M_Xactor (AXI4_M_Xactor_IFC #(wd_id, wd_addr, wd_data, wd_user)); - Addr_FIFOF_Pair #(wd_id, wd_addr, wd_user) f_rdwr_addr <- mkAddr_FIFOF_Pair; - - FIFOF #(AXI4_Wr_Addr #(wd_id, wd_addr, wd_user)) f_wr_addr = f_rdwr_addr.ff_write; - FIFOF #(AXI4_Wr_Data #(wd_data, wd_user)) f_wr_data <- mkFIFOF; - FIFOF #(AXI4_Wr_Resp #(wd_id, wd_user)) f_wr_resp <- mkFIFOF; - - FIFOF #(AXI4_Rd_Addr #(wd_id, wd_addr, wd_user)) f_rd_addr = f_rdwr_addr.ff_read; - FIFOF #(AXI4_Rd_Data #(wd_id, wd_data, wd_user)) f_rd_data <- mkFIFOF; - - AXI4_M_IFC #(wd_id, wd_addr, wd_data, wd_user) m_xactor <- mkAXI4_M_Xactor_3 (f_wr_addr, - f_wr_data, - f_wr_resp, - f_rd_addr, - f_rd_data); - - // ---------------------------------------------------------------- - // INTERFACE - - method Action reset; - f_wr_addr.clear; - f_wr_data.clear; - f_wr_resp.clear; - //f_rd_addr.clear; - f_rd_data.clear; - endmethod - - // AXI side - interface axi_side = m_xactor; - - // FIFOF side - interface i_wr_addr = to_FIFOF_I (f_wr_addr); - interface i_wr_data = to_FIFOF_I (f_wr_data); - interface o_wr_resp = to_FIFOF_O (f_wr_resp); - - interface i_rd_addr = to_FIFOF_I (f_rd_addr); - interface o_rd_data = to_FIFOF_O (f_rd_data); -endmodule diff --git a/Libraries/AMBA_Fabrics/AXI4/AXI4_Fabric.bsv b/Libraries/AMBA_Fabrics/AXI4/AXI4_Fabric.bsv index 9f972a9..3d1b015 100644 --- a/Libraries/AMBA_Fabrics/AXI4/AXI4_Fabric.bsv +++ b/Libraries/AMBA_Fabrics/AXI4/AXI4_Fabric.bsv @@ -1,470 +1,402 @@ // Copyright (c) 2013-2023 Bluespec, Inc. All Rights Reserved -// +// Copyright (c) 2024 Rishiyur S. Nikhil. + // SPDX-License-Identifier: BSD-3-Clause package AXI4_Fabric; -// ================================================================ -// This package defines a fabric connecting CPUs, Memories and DMAs -// and other IP blocks. +// **************************************************************** +// This package defines a module mkAXI4_Fabric with an Empty interface. +// It is a crossbar connecting a vector of Ms to a vector of Ss. -// ================================================================ +// It is parameterized by: +// * number of Ms +// * number of Ss +// * widths of the various AXI4 buses. +// * a "routing function" address -> S num that specifies which S +// (or none) services a request. + +// Note that M-side ID fields (ARID, AWID) are narrower than S-side ID +// fields (RID, BID). The extra S-side ID bits encode, for each AXI4 +// transaction, which M it came from, so that the response can be +// routed back appropriately. + +// Handles bursts (read and write). + +// **************************************************************** // Bluespec library imports import Vector :: *; import FIFOF :: *; import SpecialFIFOs :: *; -import ConfigReg :: *; // ---------------- -// BSV additional libs +// Bluespec misc. libs -import Cur_Cycle :: *; +import Semi_FIFOF :: *; -// ================================================================ +// ---------------- // Project imports -import Semi_FIFOF :: *; import AXI4_Types :: *; // ================================================================ -// The interface for the fabric module - -interface AXI4_Fabric_IFC #(numeric type tn_num_M, - numeric type tn_num_S, - numeric type wd_id, - numeric type wd_addr, - numeric type wd_data, - numeric type wd_user); - method Action reset; - method Action set_verbosity (Bit #(4) verbosity); - - // From Ms - interface Vector #(tn_num_M, - AXI4_S_IFC #(wd_id, wd_addr, wd_data, wd_user)) v_from_Ms; - - // To Ss - interface Vector #(tn_num_S, - AXI4_M_IFC #(wd_id, wd_addr, wd_data, wd_user)) v_to_Ss; -endinterface +// Project exports -// ================================================================ +export mkAXI4_Fabric; + +// **************************************************************** // The Fabric module // The function parameter is an address-decode function, which -// returns (True, S-port-num) if address is mapped to S-port-num -// (False, ?) if address is unmapped to any S port - -module mkAXI4_Fabric #(function Tuple2 #(Bool, Bit #(TLog #(tn_num_S))) - fn_addr_to_S_num (Bit #(wd_addr) addr)) - (AXI4_Fabric_IFC #(tn_num_M, tn_num_S, - wd_id, wd_addr, wd_data, wd_user)) - - provisos (Log #(tn_num_M, log_nm), - Log #(tn_num_S, log_ns), - Log #(TAdd #(tn_num_S, 1), log_ns_plus_1), - Log #(TAdd #(tn_num_M, 1), log_nm_plus_1)); - - Integer num_M = valueOf (tn_num_M); - Integer num_S = valueOf (tn_num_S); +// returns (True, j) if address maps to Sj +// (False, ?) if address is wild (does not map to any Sj) + +module mkAXI4_Fabric #(// Routing function + function Tuple2 #(Bool, Bit #(TLog #(tn_num_S))) + fn_addr_to_S_num (Bit #(wd_addr) addr), + // From Ms + Vector #(tn_num_M, + AXI4_M_IFC #(wd_id_M, wd_addr, wd_data, wd_user)) v_ifc_M, + // To Ss + Vector #(tn_num_S, + AXI4_S_IFC #(wd_id_S, wd_addr, wd_data, wd_user)) v_ifc_S) + (Empty) + + provisos (Log #(tn_num_M, log_nm), // define log_nm + Log #(tn_num_S, log_ns), // define log_ns + Add #(wd_id_M, log_nm, wd_id_S)); // assert // 0: quiet; 1: show transactions - Reg #(Bit #(4)) cfg_verbosity <- mkConfigReg (0); + Integer verbosity = 0; - Reg #(Bool) rg_reset <- mkReg (True); - - // Transactors facing Ms - Vector #(tn_num_M, AXI4_S_Xactor_IFC #(wd_id, wd_addr, wd_data, wd_user)) - xactors_from_Ms <- replicateM (mkAXI4_S_Xactor); - - // Transactors facing Ss - Vector #(tn_num_S, AXI4_M_Xactor_IFC #(wd_id, wd_addr, wd_data, wd_user)) - xactors_to_Ss <- replicateM (mkAXI4_M_Xactor); + Integer num_M = valueOf (tn_num_M); + Integer num_S = valueOf (tn_num_S); // ---------------------------------------------------------------- - // Book-keeping FIFOs and regs - // - to keep track of which M originated a transaction, in order - // to route corresponding responses back to that M - // - to manage wdata channel based on burst info in awaddr channel - // - to manage requests that do not map to any of the Ss - // Legal Ss are 0..(num_S-1) - // The "illegal" value of 'num_S' is used for decode errors (no such S). - // num_M could be 1 => Bit #(0) to identify a M, but - // equality on Bit #(0) is dicey, so we always use num_M+1. - // Size of SizedFIFOs is estimated: should cover round-trip latency to S and back. - - // ---------------- - // Write-transaction book-keeping - - // On an mi->sj write-transaction, this fifo records sj for M mi - Vector #(tn_num_M, FIFOF #(Bit #(log_ns_plus_1))) v_f_wr_sjs <- replicateM (mkSizedFIFOF (8)); + // Write-transaction control: - // On an mi->sj write-transaction, this fifo records mi for S sj - Vector #(tn_num_S, FIFOF #(Bit #(log_nm_plus_1))) v_f_wr_mis <- replicateM (mkSizedFIFOF (8)); + // The AW and W buses are separate and not synchronized, but the + // ordering is the same, i.e., in any AXI4 interface, the sequence + // of W bursts (W0, W1, ...) is assumed to correspond to the + // sequence of AW requests (AW0, AW1, ...). - // On an mi->sj write-transaction, this fifo records a task (sj, awlen) for W channel - Vector #(tn_num_M, - FIFOF #(Tuple2 #(Bit #(log_ns_plus_1), - AXI4_Len))) v_f_wd_tasks <- replicateM (mkFIFOF); - // On an mi->sj write-transaction, this register is the W-channel burst beat_count - // (0 => ready for next burst) - Vector #(tn_num_M, Reg #(AXI4_Len)) v_rg_wd_beat_count <- replicateM (mkReg (0)); + // In a fabric, two write-transaction scenarios where the ordering could go wrong: + // A. Two M's to one S: + // From Mi and Mj, if we send AWi and AWj to Sk, in that order, + // then Wi should precede Wj. + // Also: beats from the Wi and Wj bursts should not be interleaved. + // B. One M to two S's: + // From Mi, if we send AWi1 and AWi2 to Sj and Sk, in that order, + // then Wi1 should go to Sj and Wi2 should go to Sk + // - // On a write-transaction to non-existent S, record id and user for error response - Vector #(tn_num_M, - FIFOF #(Tuple2 #(Bit #(wd_id), - Bit #(wd_user)))) v_f_wr_err_info <- replicateM (mkSizedFIFOF (8)); + // For scenario A, we have a FIFO per-Sj which records order of M's + // from which it should take Ws. - // ---------------- - // Read-transaction book-keeping - - // On an mi->sj read-transaction, records sj for M mi - Vector #(tn_num_M, FIFOF #(Bit #(log_ns_plus_1))) v_f_rd_sjs <- replicateM (mkSizedFIFOF (8)); - // On an mi->sj read-transaction, records (mi,arlen) for S sj - Vector #(tn_num_S, - FIFOF #(Tuple2 #(Bit #(log_nm_plus_1), - AXI4_Len))) v_f_rd_mis <- replicateM (mkSizedFIFOF (8)); - // On an mi->sj read-transaction, this register is the R-channel burst beat_count - // (0 => ready for next burst) - Vector #(tn_num_S, Reg #(AXI4_Len)) v_rg_r_beat_count <- replicateM (mkReg (0)); - - // On a read-transaction to non-exisitent S, record id and user for error response - Vector #(tn_num_M, - FIFOF #(Tuple3 #(AXI4_Len, - Bit #(wd_id), - Bit #(wd_user)))) v_f_rd_err_info <- replicateM (mkSizedFIFOF (8)); - - // On an mi->non-existent-S read-transaction, - // this register is the R-channel burst beat_count - // (0 => ready for next burst) - Vector #(tn_num_M, Reg #(AXI4_Len)) v_rg_r_err_beat_count <- replicateM (mkReg (0)); + Vector #(tn_num_S, FIFOF #(Bit #(log_nm))) // FIFO of Mi + v_f_W_Mi <- replicateM (mkFIFOF); - // ---------------------------------------------------------------- - // RESET + // For scenario B, we have a FIFO per-Mi which records order of S's + // to which it should send Ws. - rule rl_reset (rg_reset); - if (cfg_verbosity > 0) begin - $display ("%0d: rl_reset", cur_cycle); - $display (" %m"); - end - for (Integer mi = 0; mi < num_M; mi = mi + 1) begin - xactors_from_Ms [mi].reset; + Vector #(tn_num_M, FIFOF #(Tuple2 #(Bool, Bit #(log_ns)))) // FIFO of Sj + v_f_W_Sj <- replicateM (mkFIFOF); - v_f_wr_sjs [mi].clear; - v_f_wd_tasks [mi].clear; - v_rg_wd_beat_count [mi] <= 0; + // For wild writes (addr does not map to any Sj), this FIFO records + // info to consume the write-burst and then respond with error. + // TODO: per-Mi FIFOs would add concurrency. - v_f_wr_err_info [mi].clear; + FIFOF #(Tuple3 #(Bit #(log_nm), // Mi of current AWCHAN req + Bit #(wd_id_M), // AWID to reflect into BID + Bit #(wd_user))) // AWUSER to reflect into BUSER + f_W_wild <- mkFIFOF; - v_f_rd_sjs [mi].clear; + // ---------------- + // Read-respose merge control - v_f_rd_err_info [mi].clear; - end + // For "simultaneous" RCHAN responses from Sj1 and Sj2 to the same + // Mi, the two bursts must not interleave. Each Sj must "own" Mi + // for its full burst. - for (Integer sj = 0; sj < num_S; sj = sj + 1) begin - xactors_to_Ss [sj].reset; - v_f_wr_mis [sj].clear; - v_f_rd_mis [sj].clear; - v_rg_r_beat_count [sj] <= 0; - end - rg_reset <= False; - endrule + Vector #(tn_num_M, Reg #(Maybe #(Bit #(log_ns)))) + v_rg_M_RCHAN_owners <- replicateM (mkReg (tagged Invalid)); // ---------------------------------------------------------------- - // BEHAVIOR + // Predicates to check if Mi has transaction for Sj + // The expression (s_num == fromInteger (sj)) is dodgy when num_S == 1 + // because it's a Bit#(0) comparision; so special case 'num_S == 1' - // ---------------------------------------------------------------- - // Predicates to check if M I has transaction for S J function Bool fv_mi_has_wr_for_sj (Integer mi, Integer sj); - let addr = xactors_from_Ms [mi].o_wr_addr.first.awaddr; + let addr = v_ifc_M [mi].o_AW.first.awaddr; match { .legal, .s_num } = fn_addr_to_S_num (addr); return (legal && ( (num_S == 1) || (s_num == fromInteger (sj)))); endfunction - function Bool fv_mi_has_wr_for_none (Integer mi); - let addr = xactors_from_Ms [mi].o_wr_addr.first.awaddr; - match { .legal, ._ } = fn_addr_to_S_num (addr); - return (! legal); - endfunction - function Bool fv_mi_has_rd_for_sj (Integer mi, Integer sj); - let addr = xactors_from_Ms [mi].o_rd_addr.first.araddr; + let addr = v_ifc_M [mi].o_AR.first.araddr; match { .legal, .s_num } = fn_addr_to_S_num (addr); return (legal && ( (num_S == 1) || (s_num == fromInteger (sj)))); endfunction + function Bool fv_mi_has_wr_for_none (Integer mi); + let addr = v_ifc_M [mi].o_AW.first.awaddr; + match { .legal, ._ } = fn_addr_to_S_num (addr); + return (! legal); + endfunction + function Bool fv_mi_has_rd_for_none (Integer mi); - let addr = xactors_from_Ms [mi].o_rd_addr.first.araddr; + let addr = v_ifc_M [mi].o_AR.first.araddr; match { .legal, ._ } = fn_addr_to_S_num (addr); return (! legal); endfunction // ================================================================ - // Wr requests (AW, W and B channels) + // BEHAVIOR: AWCHAN (write-requests) - // Wr requests to legal Ss (AW channel) - for (Integer mi = 0; mi < num_M; mi = mi + 1) - for (Integer sj = 0; sj < num_S; sj = sj + 1) + Rules all_rules = emptyRules; - rule rl_wr_xaction_M_to_S (fv_mi_has_wr_for_sj (mi, sj)); - // Move the AW transaction - AXI4_Wr_Addr #(wd_id, wd_addr, wd_user) a <- pop_o (xactors_from_Ms [mi].o_wr_addr); - xactors_to_Ss [sj].i_wr_addr.enq (a); - - // Enqueue a task for the W channel - v_f_wd_tasks [mi].enq (tuple2 (fromInteger (sj), a.awlen)); - - // Book-keeping - v_f_wr_mis [sj].enq (fromInteger (mi)); - v_f_wr_sjs [mi].enq (fromInteger (sj)); - - if (cfg_verbosity > 0) begin - $display ("%0d: rl_wr_xaction_M_to_S: m%0d -> s%0d", cur_cycle, mi, sj); - $display (" %m"); - $display (" ", fshow (a)); - end - endrule - - // Wr requests to non-existent S (AW channel) - for (Integer mi = 0; mi < num_M; mi = mi + 1) - rule rl_wr_xaction_no_such_S (fv_mi_has_wr_for_none (mi)); - AXI4_Wr_Addr #(wd_id, wd_addr, wd_user) a <- pop_o (xactors_from_Ms [mi].o_wr_addr); - - // Special value 'num_S' (not a legal sj) means "no such S" - v_f_wr_sjs [mi].enq (fromInteger (num_S)); - v_f_wr_err_info [mi].enq (tuple2 (a.awid, a.awuser)); - - // Enqueue a task for the W channel (must consume the write-data burst) - v_f_wd_tasks [mi].enq (tuple2 (fromInteger (num_S), a.awlen)); - - $display ("%0d: ERROR: rl_wr_xaction_no_such_S: m%0d -> ?", cur_cycle, mi); - $display (" %m"); - $display (" ", fshow (a)); - endrule - - // Wr data (W channel) - for (Integer mi = 0; mi < num_M; mi = mi + 1) - - // Handle W channel burst - // Invariant: v_rg_wd_beat_count == 0 between bursts - // Note: awlen is encoded as 0..255 for burst lengths of 1..256 - rule rl_wr_xaction_M_to_S_data (v_f_wd_tasks [mi].first matches {.sj, .awlen}); - AXI4_Wr_Data #(wd_data, wd_user) d <- pop_o (xactors_from_Ms [mi].o_wr_data); - - // If sj is a legal S, send it the data beat, else drop it. - if (sj < fromInteger (num_S)) - xactors_to_Ss [sj].i_wr_data.enq (d); - - if (cfg_verbosity > 0) begin - $display ("%0d: rl_wr_xaction_M_to_S_data: m%0d -> s%0d, beat %0d/%0d", - cur_cycle, mi, sj, v_rg_wd_beat_count [mi], awlen); - $display (" %m"); - $display (" ", fshow (d)); - end - - if (v_rg_wd_beat_count [mi] == awlen) begin - // End of burst - v_f_wd_tasks [mi].deq; - v_rg_wd_beat_count [mi] <= 0; - - // Simulation-only assertion-check (no action, just display assertion failure) - // Final beat must have WLAST = 1 - // Rely on S (which should also see this error) to return error response - if (! (d.wlast)) begin - $display ("%0d: ERROR: rl_wr_xaction_M_to_S_data: m%0d -> s%0d", - cur_cycle, mi, sj); - $display (" WLAST not set on final data beat (awlen = %0d)", awlen); - $display (" %m"); - $display (" ", fshow (d)); - end - end - else - v_rg_wd_beat_count [mi] <= v_rg_wd_beat_count [mi] + 1; - endrule - - // Wr responses from Ss to Ms (B channel) - - for (Integer mi = 0; mi < num_M; mi = mi + 1) + // AW legal addrs + for (Integer mi = 0; mi < num_M; mi = mi + 1) begin for (Integer sj = 0; sj < num_S; sj = sj + 1) - - rule rl_wr_resp_S_to_M ( (v_f_wr_mis [sj].first == fromInteger (mi)) - && (v_f_wr_sjs [mi].first == fromInteger (sj))); - v_f_wr_mis [sj].deq; - v_f_wr_sjs [mi].deq; - AXI4_Wr_Resp #(wd_id, wd_user) b <- pop_o (xactors_to_Ss [sj].o_wr_resp); - - xactors_from_Ms [mi].i_wr_resp.enq (b); - - if (cfg_verbosity > 0) begin - $display ("%0d: rl_wr_resp_S_to_M: m%0d <- s%0d", - cur_cycle, mi, sj); - $display (" %m"); - $display (" ", fshow (b)); - end - endrule - - // Wr error responses to Ms (B channel) - // v_f_wr_sjs [mi].first has value num_S (illegal value) - // v_f_wr_err_info [mi].first contains request fields 'awid' and 'awuser' - - for (Integer mi = 0; mi < num_M; mi = mi + 1) - - rule rl_wr_resp_err_to_M (v_f_wr_sjs [mi].first == fromInteger (num_S)); - v_f_wr_sjs [mi].deq; - v_f_wr_err_info [mi].deq; - - match { .awid, .awuser } = v_f_wr_err_info [mi].first; - - let b = AXI4_Wr_Resp {bid: awid, - bresp: axi4_resp_decerr, - buser: awuser}; - - xactors_from_Ms [mi].i_wr_resp.enq (b); - - if (cfg_verbosity > 0) begin - $display ("%0d: rl_wr_resp_err_to_M: m%0d <- err", cur_cycle, mi); - $display (" %m"); - $display (" ", fshow (b)); - end - endrule + all_rules = + rJoinDescendingUrgency ( + all_rules, + rules + rule rl_AW (fv_mi_has_wr_for_sj (mi, sj)); + // Forward the AW transaction + let aw_in <- pop_o (v_ifc_M [mi].o_AW); + let awid_S = { aw_in.awid, fromInteger (mi) }; + let aw_out = fn_change_AW_id (aw_in, awid_S); + v_ifc_S [sj].i_AW.enq (aw_out); + + // Enqueue mi->sj control info for W channel + v_f_W_Sj [mi].enq (tuple2 (True, fromInteger (sj))); + v_f_W_Mi [sj].enq (fromInteger (mi)); + + if (verbosity > 0) begin + $display ("AXI4_Fabric: AW m%0d -> s%0d", mi, sj); + $display (" ", fshow (aw_in)); + end + endrule + endrules); + + // AW wild addrs (awaddr does not map to any Sj) + all_rules = + rJoinDescendingUrgency ( + all_rules, + rules + rule rl_AW_wild (fv_mi_has_wr_for_none (mi)); + let aw_in <- pop_o (v_ifc_M [mi].o_AW); + + // Enqueue mi->sj control info for W channel + v_f_W_Sj [mi].enq (tuple2 (False, ?)); + f_W_wild.enq (tuple3 (fromInteger (mi), aw_in.awid, aw_in.awuser)); + + if (verbosity > 0) begin + $display ("ERROR: AXI4_Fabric: AW m%0d -> wild", mi); + $display (" ", fshow (aw_in)); + end + endrule + endrules); + end // ================================================================ - // Rd requests (AR and R channels) + // BEHAVIOR: WCHAN (write-data) - // Rd requests to legal Ss (AR channel) - for (Integer mi = 0; mi < num_M; mi = mi + 1) + // W normal + for (Integer mi = 0; mi < num_M; mi = mi + 1) begin for (Integer sj = 0; sj < num_S; sj = sj + 1) + all_rules = + rJoinDescendingUrgency ( + all_rules, + rules + + // W channel (with bursts) + // Invariant: v_rg_wd_beat_count == 0 between bursts + // Note: awlen encodes burst lengths of 1..256 as 0..255 + rule rl_W ((v_f_W_Mi [sj].first == fromInteger (mi)) + && tpl_1 (v_f_W_Sj [mi].first) + && (tpl_2 (v_f_W_Sj [mi].first) == fromInteger (sj))); + + // Forward the W + let w <- pop_o (v_ifc_M [mi].o_W); + v_ifc_S [sj].i_W.enq (w); + + if (verbosity > 0) begin + $display ("AXI4_Fabric: W m%0d -> s%0d", mi, sj); + $display (" ", fshow (w)); + end + + if (w.wlast) begin + // End of burst; dequeue the control info + v_f_W_Mi [sj].deq; + v_f_W_Sj [mi].deq; + end + endrule + endrules); + + // W for wild addrs + all_rules = + rJoinDescendingUrgency ( + all_rules, + rules + rule rl_W_wild (tpl_1 (f_W_wild.first) == fromInteger (mi) + && (! tpl_1 (v_f_W_Sj [mi].first))); + + match { .mi, .bid, .buser } = f_W_wild.first; + + // Consume the W and drop it + let w <- pop_o (v_ifc_M [mi].o_W); + + if (verbosity > 0) begin + $display ("AXI4_Fabric: W m%0d -> WILD", mi); + $display (" ", fshow (w)); + end - rule rl_rd_xaction_M_to_S (fv_mi_has_rd_for_sj (mi, sj)); - AXI4_Rd_Addr #(wd_id, wd_addr, wd_user) a <- pop_o (xactors_from_Ms [mi].o_rd_addr); - - xactors_to_Ss [sj].i_rd_addr.enq (a); - - v_f_rd_mis [sj].enq (tuple2 (fromInteger (mi), a.arlen)); - v_f_rd_sjs [mi].enq (fromInteger (sj)); - - if (cfg_verbosity > 0) begin - $display ("%0d: rl_rd_xaction_M_to_S: m%0d -> s%0d", - cur_cycle, mi, sj); - $display (" %m"); - $display (" ", fshow (a)); - end - endrule - - // Rd requests to non-existent S (AR channel) - for (Integer mi = 0; mi < num_M; mi = mi + 1) - rule rl_rd_xaction_no_such_S (fv_mi_has_rd_for_none (mi)); - AXI4_Rd_Addr #(wd_id, wd_addr, wd_user) a <- pop_o (xactors_from_Ms [mi].o_rd_addr); + if (w.wlast) begin + // End of burst; dequeue the control info + f_W_wild.deq; + v_f_W_Sj [mi].deq; - v_f_rd_sjs [mi].enq (fromInteger (num_S)); - v_f_rd_err_info [mi].enq (tuple3 (a.arlen, a.arid, a.aruser)); + // Send error response to Mi + let b = AXI4_B {bid: bid, + bresp: axi4_resp_decerr, + buser: buser}; + v_ifc_M [mi].i_B.enq (b); + end + endrule + endrules); + end - $display ("%0d: ERROR: rl_rd_xaction_no_such_S: m%0d -> ?", cur_cycle, mi); - $display (" %m"); - $display (" ", fshow (a)); - endrule + // ================================================================ + // BEHAVIOR: BCHAN (write-responses) + + // Wr responses from Ss to Ms + for (Integer sj = 0; sj < num_S; sj = sj + 1) + all_rules = + rJoinDescendingUrgency ( + all_rules, + rules + rule rl_B; + // Incoming BCHAN response + AXI4_B #(wd_id_S, wd_user) b_in <- pop_o (v_ifc_S [sj].o_B); + // Extract mi and bid_M from incoming bid_S + Bit #(wd_id_S) bid_S = b_in.bid; + Bit #(log_nm) mi = truncate (bid_S); + Bit #(wd_id_M) bid_M = truncateLSB (bid_S); + // Replace bid_S with bid_M in BCHAN response, send to mi + AXI4_B #(wd_id_M, wd_user) b_out = fn_change_B_id (b_in, bid_M); + v_ifc_M [mi].i_B.enq (b_out); + + if (verbosity > 0) begin + $display ("AXI4_Fabric: B m%0d <- s%0d", mi, sj); + $display (" ", fshow (b_out)); + end + endrule + endrules); - // Rd responses from Ss to Ms (R channel) + // ================================================================ + // ARCHAN (read requests) - for (Integer mi = 0; mi < num_M; mi = mi + 1) + // Legal addrs + for (Integer mi = 0; mi < num_M; mi = mi + 1) begin for (Integer sj = 0; sj < num_S; sj = sj + 1) - - rule rl_rd_resp_S_to_M (v_f_rd_mis [sj].first matches { .mi2, .arlen } - &&& (mi2 == fromInteger (mi)) - &&& (v_f_rd_sjs [mi].first == fromInteger (sj))); - - AXI4_Rd_Data #(wd_id, wd_data, wd_user) r <- pop_o (xactors_to_Ss [sj].o_rd_data); - - if (v_rg_r_beat_count [sj] == arlen) begin - // Final beat of burst - v_f_rd_mis [sj].deq; - v_f_rd_sjs [mi].deq; - v_rg_r_beat_count [sj] <= 0; - - // Assertion-check - // Final beat must have RLAST = 1 - // If not, and if RRESP is OK, set RRESP to AXI4_RESP_SLVERR - if ((r.rresp == axi4_resp_okay) && (! (r.rlast))) begin - r.rresp = axi4_resp_slverr; - $display ("%0d: ERROR: rl_rd_resp_S_to_M: m%0d <- s%0d", - cur_cycle, mi, sj); - $display (" RLAST not set on final data beat (arlen = %0d)", arlen); - $display (" %m"); - $display (" ", fshow (r)); + all_rules = + rJoinDescendingUrgency ( + all_rules, + rules + rule rl_AR (fv_mi_has_rd_for_sj (mi, sj)); + let ar_in <- pop_o (v_ifc_M [mi].o_AR); + let arid_S = { ar_in.arid, fromInteger (mi) }; + let ar_out = fn_change_AR_id (ar_in, arid_S); + v_ifc_S [sj].i_AR.enq (ar_out); + + if (verbosity > 0) begin + $display ("AXI4_Fabric: AR m%0d -> s%0d", mi, sj); + $display (" ", fshow (ar_in)); + end + endrule + endrules); + + all_rules = + rJoinDescendingUrgency ( + all_rules, + rules + // Wild addr (araddr does not map to any Sj) + rule rl_AR_wild (fv_mi_has_rd_for_none (mi)); + let ar <- pop_o (v_ifc_M [mi].o_AR); + let r = AXI4_R {rid: ar.arid, + rdata: ?, + rresp: axi4_resp_decerr, + rlast: True, + ruser: ar.aruser}; + v_ifc_M [mi].i_R.enq (r); + + if (verbosity > 0) begin + $display ("ERROR: AR m%0d -> WILD", mi); + $display (" ", fshow (ar)); end - end - else - v_rg_r_beat_count [sj] <= v_rg_r_beat_count [sj] + 1; - - xactors_from_Ms [mi].i_rd_data.enq (r); - - if (cfg_verbosity > 0) begin - $display ("%0d: rl_rd_resp_S_to_M: m%0d <- s%0d", - cur_cycle, mi, sj); - $display (" %m"); - $display (" r: ", fshow (r)); - end - endrule - - // Rd error responses to Ms (R channel) - // v_f_rd_sjs [mi].first has value num_S (illegal value) - // v_f_rd_err_info [mi].first contains request fields: 'arlen', 'arid', 'aruser' - - for (Integer mi = 0; mi < num_M; mi = mi + 1) - - rule rl_rd_resp_err_to_M (v_f_rd_sjs [mi].first == fromInteger (num_S)); - match { .arlen, .arid, .aruser } = v_f_rd_err_info [mi].first; - - Bit #(wd_data) data = 0; - let r = AXI4_Rd_Data {rid: arid, - rdata: data, - rresp: axi4_resp_decerr, - rlast: (v_rg_r_err_beat_count [mi] == arlen), - ruser: aruser}; - - xactors_from_Ms [mi].i_rd_data.enq (r); - - if (v_rg_r_err_beat_count [mi] == arlen) begin - // Last beat of burst - v_f_rd_sjs [mi].deq; - v_f_rd_err_info [mi].deq; - v_rg_r_err_beat_count [mi] <= 0; - end - else - v_rg_r_err_beat_count [mi] <= v_rg_r_err_beat_count [mi] + 1; - - if (cfg_verbosity > 0) begin - $display ("%0d: rl_rd_resp_err_to_M: m%0d <- err", - cur_cycle, mi); - $display (" %m"); - $display (" r: ", fshow (r)); - end - endrule + endrule + endrules); + end // ================================================================ - // INTERFACE - - function AXI4_S_IFC #(wd_id, wd_addr, wd_data, wd_user) f1 (Integer j) - = xactors_from_Ms [j].axi_side; - function AXI4_M_IFC #(wd_id, wd_addr, wd_data, wd_user) f2 (Integer j) - = xactors_to_Ss [j].axi_side; + // RCHAN (read responses) + + for (Integer sj = 0; sj < num_S; sj = sj + 1) + all_rules = + rJoinDescendingUrgency ( + all_rules, + rules + rule rl_R; + AXI4_R #(wd_id_S, wd_data, wd_user) + r_in = v_ifc_S [sj].o_R.first; + + // Check if we already own Mi or if Mi is free (no owner) + Bit #(log_nm) mi = truncate (r_in.rid); + Bool we_own = False; + if (v_rg_M_RCHAN_owners [mi] matches tagged Invalid) + we_own = True; + else if (v_rg_M_RCHAN_owners [mi] matches tagged Valid .sj2 + &&& (sj2 == fromInteger (sj))) + we_own = True; + + if (we_own) begin + // Forward the R response + v_ifc_S [sj].o_R.deq; + Bit #(wd_id_S) rid_S = r_in.rid; + Bit #(wd_id_M) rid_M = truncateLSB (rid_S); + let r_M = fn_change_R_id (r_in, rid_M); + v_ifc_M [mi].i_R.enq (r_M); + + // Release ownership of Mi RCHAN on last beat + if (r_in.rlast) + v_rg_M_RCHAN_owners [mi] <= tagged Invalid; + else + // Record ownership for rest of beats + v_rg_M_RCHAN_owners [mi] <= tagged Valid (fromInteger (sj)); + + if (verbosity > 0) begin + $display ("AXI4_Fabric: R m%0d <- s%0d", mi, sj); + $display (" ", fshow (r_M)); + end + end + endrule + endrules); - method Action reset () if (! rg_reset); - rg_reset <= True; - endmethod + addRules (all_rules); - method Action set_verbosity (Bit #(4) verbosity); - cfg_verbosity <= verbosity; - endmethod + // **************************************************************** + // INTERFACE - interface v_from_Ms = genWith (f1); - interface v_to_Ss = genWith (f2); + // Empty endmodule -// ================================================================ +// **************************************************************** endpackage: AXI4_Fabric diff --git a/Libraries/AMBA_Fabrics/AXI4/AXI4_Gate.bsv b/Libraries/AMBA_Fabrics/AXI4/AXI4_Gate.bsv index 27b38c9..dc199a0 100644 --- a/Libraries/AMBA_Fabrics/AXI4/AXI4_Gate.bsv +++ b/Libraries/AMBA_Fabrics/AXI4/AXI4_Gate.bsv @@ -1,23 +1,25 @@ -// Copyright (c) 2021-2023 Bluespec, Inc. All Rights Reserved +// Copyright (c) 2021-2024 Bluespec, Inc. All Rights Reserved // Author: Rishiyur S. Nikhil -// + // SPDX-License-Identifier: BSD-3-Clause package AXI4_Gate; // ================================================================ -// This package defines an AXI4-M-to-AXI4-S 'gate' module, -// that either allows or blocks the 5 AXI4 buses, -// depending on a Bool 'enable' input. +// This package defines a 'gate' module connecting an ifc_M to an ifc_S. +// When driving method 'm_enable(True)' it passes AXI4 traffic through. +// When driving method 'm_enable(False)' it blocks AXI4 traffic. + +// Can be used to control authorized access to an AXI4 connection. // ================================================================ // Bluespec library imports -import Vector :: *; -import FIFOF :: *; +import Vector :: *; +import FIFOF :: *; // ---------------- -// BSV additional libs +// Bluespec misc. libs import Cur_Cycle :: *; import GetPut_Aux :: *; @@ -29,17 +31,16 @@ import Semi_FIFOF :: *; import AXI4_Types :: *; // ================================================================ -// The interface for the gate module -interface AXI4_Gate_IFC #(numeric type wd_id_t, - numeric type wd_addr_t, - numeric type wd_data_t, - numeric type wd_user_t); - // From M - interface AXI4_S_IFC #(wd_id_t, wd_addr_t, wd_data_t, wd_user_t) axi4_S; - // To S - interface AXI4_M_IFC #(wd_id_t, wd_addr_t, wd_data_t, wd_user_t) axi4_M; +Integer verbosity = 0; + +// ================================================================ +// The interface for the gate module +interface AXI4_Gate_IFC #(numeric type wd_id, + numeric type wd_addr, + numeric type wd_data, + numeric type wd_user); // Enable control signal. Continuously driven with Bool arg. (* always_ready, always_enabled *) method Action m_enable (Bool enabled); @@ -47,139 +48,123 @@ endinterface // ================================================================ // The Gate module +// The Bool parameter: False: just block traffic; True: gen AXI4 err response -Integer verbosity = 0; - -module mkAXI4_Gate - #(Bool respond_with_err) // False: block traffic; True: respond with err - (AXI4_Gate_IFC #(wd_id_t, wd_addr_t, wd_data_t, wd_user_t)); - - // ---------------- - // Transactor facing M - AXI4_S_Xactor_IFC #(wd_id_t, wd_addr_t, wd_data_t, wd_user_t) - xactor_from_M <- mkAXI4_S_Xactor; - - // Transactor facing S - AXI4_M_Xactor_IFC #(wd_id_t, wd_addr_t, wd_data_t, wd_user_t) - xactor_to_S <- mkAXI4_M_Xactor; +module mkAXI4_Gated_Buffer #(Bool respond_with_err, + AXI4_M_IFC #(wd_id, wd_addr, wd_data, wd_user) ifc_M, + AXI4_S_IFC #(wd_id, wd_addr, wd_data, wd_user) ifc_S) + (AXI4_Gate_IFC #(wd_id, wd_addr, wd_data, wd_user)); Reg #(Bool) rg_enabled <- mkReg (False); Reg #(Bool) rg_enabled_prev <- mkReg (False); - // ---------------------------------------------------------------- + // ================================================================ // BEHAVIOR // ---------------- // When gate is enabled: pass-through everything M-to-S and S-to-M - rule rl_wr_addr (rg_enabled); - let wra <- pop_o (xactor_from_M.o_wr_addr); - xactor_to_S.i_wr_addr.enq (wra); + rule rl_AW (rg_enabled); + let wra <- pop_o (ifc_M.o_AW); + ifc_S.i_AW.enq (wra); endrule - rule rl_wr_data (rg_enabled); - let wrd <- pop_o (xactor_from_M.o_wr_data); - xactor_to_S.i_wr_data.enq (wrd); + rule rl_W (rg_enabled); + let wrd <- pop_o (ifc_M.o_W); + ifc_S.i_W.enq (wrd); endrule - rule rl_wr_resp (rg_enabled); - let wrr <- pop_o (xactor_to_S.o_wr_resp); - xactor_from_M.i_wr_resp.enq (wrr); + rule rl_B (rg_enabled); + let wrr <- pop_o (ifc_S.o_B); + ifc_M.i_B.enq (wrr); endrule - rule rl_rd_addr (rg_enabled); - let rda <- pop_o (xactor_from_M.o_rd_addr); - xactor_to_S.i_rd_addr.enq (rda); + rule rl_AR (rg_enabled); + let rda <- pop_o (ifc_M.o_AR); + ifc_S.i_AR.enq (rda); endrule - rule rl_rd_data (rg_enabled); - let rdd <- pop_o (xactor_to_S.o_rd_data); - xactor_from_M.i_rd_data.enq (rdd); + rule rl_R (rg_enabled); + let rdd <- pop_o (ifc_S.o_R); + ifc_M.i_R.enq (rdd); endrule // ---------------- // When gate is disabled: return error responses to M; // don't send anything to S or expect anything from S. - rule rl_wr_addr_disabled (respond_with_err && (! rg_enabled)); - let wra <- pop_o (xactor_from_M.o_wr_addr); - let wrr = AXI4_Wr_Resp {bid: wra.awid, - bresp: axi4_resp_slverr, - buser: wra.awuser}; - xactor_from_M.i_wr_resp.enq (wrr); - - $display ("WARNING: rl_wr_addr_disabled: rec'd wr request from M when gate disabled."); - $display (" ", fshow (wra)); - $display (" Returning error response."); - $display (" %0d: %m", cur_cycle); + rule rl_AW_disabled (respond_with_err && (! rg_enabled)); + let aw <- pop_o (ifc_M.o_AW); + let b = AXI4_B {bid: aw.awid, + bresp: axi4_resp_slverr, + buser: aw.awuser}; + ifc_M.i_B.enq (b); + + $display ("WARNING: rl_AW_disabled: rec'd wr request from M when gate disabled."); + $display (" ", fshow (aw)); + $display (" %0d: Returning error response.", cur_cycle); endrule - rule rl_wr_data_disabled (respond_with_err && (! rg_enabled)); - let wrd <- pop_o (xactor_from_M.o_wr_data); + rule rl_W_disabled (respond_with_err && (! rg_enabled)); + let w <- pop_o (ifc_M.o_W); // Discard the data endrule - rule rl_wr_resp_disabled_drain_S (respond_with_err && (! rg_enabled)); - let wrr <- pop_o (xactor_to_S.o_wr_resp); - $display ("WARNING: rl_wr_resp_disabled: rec'd wr resp from S when gate disabled; ignoring"); - $display (" (there couldn't have been a request)"); - $display (" %0d: %m", cur_cycle); + rule rl_B_disabled_drain_S (respond_with_err && (! rg_enabled)); + let b <- pop_o (ifc_S.o_B); + $display ("WARNING: rl_B_disabled: rec'd wr resp from S when gate disabled; ignoring"); + $display (" %0d: (there couldn't have been a request)", cur_cycle); endrule Reg #(Bit #(9)) rg_rd_burst_len <- mkRegU; - rule rl_rd_addr_disabled (respond_with_err && (! rg_enabled)); - let rda = xactor_from_M.o_rd_addr.first; + rule rl_AR_disabled (respond_with_err && (! rg_enabled)); + let ar = ifc_M.o_AR.first; // Pop this request only after sending burst responses // Note: AXI4 decodes burst len = arlen + 1 - rg_rd_burst_len <= zeroExtend (rda.arlen) + 1; + rg_rd_burst_len <= zeroExtend (ar.arlen) + 1; $display ("WARNING: rl_rd_addr_disabled: rec'd rd request from M when gate disabled."); - $display (" ", fshow (rda)); - $display (" Returning error response."); - $display (" %0d: %m", cur_cycle); + $display (" ", fshow (ar)); + $display (" %0d: Returning error response.", cur_cycle); endrule // Send burst of responses - rule rl_rd_data_disabled_burst_resps (respond_with_err - && (! rg_enabled) - && (rg_rd_burst_len != 0)); - let rda = xactor_from_M.o_rd_addr.first; - Bit #(wd_data_t) rdata = ?; - let rdd = AXI4_Rd_Data {rid: rda.arid, - rresp: axi4_resp_slverr, - rdata: rdata, - rlast: (rg_rd_burst_len == 1), - ruser: rda.aruser}; - xactor_from_M.i_rd_data.enq (rdd); - - if (rdd.rlast) + rule rl_R_disabled_burst_resps (respond_with_err + && (! rg_enabled) + && (rg_rd_burst_len != 0)); + let ar = ifc_M.o_AR.first; + Bit #(wd_data) rdata = ?; + let r = AXI4_R {rid: ar.arid, + rresp: axi4_resp_slverr, + rdata: rdata, + rlast: (rg_rd_burst_len == 1), + ruser: ar.aruser}; + ifc_M.i_R.enq (r); + + if (r.rlast) // Consume the request - xactor_from_M.o_rd_addr.deq; + ifc_M.o_AR.deq; else rg_rd_burst_len <= rg_rd_burst_len - 1; endrule - rule rl_rd_data_disabled_drain_S (respond_with_err && (! rg_enabled)); - let rdd <- pop_o (xactor_to_S.o_rd_data); - $display ("WARNING: rl_rd_data_disabled: rec'd rd resp from S when gate disabled; ignoring"); - $display (" (there couldn't have been a request)"); - $display (" %0d: %m", cur_cycle); + rule rl_R_disabled_drain_S (respond_with_err && (! rg_enabled)); + let r <- pop_o (ifc_S.o_R); + $display ("WARNING: rl_R_disabled: rec'd rd resp from S when gate disabled; ignoring"); + $display (" %0d: (there couldn't have been a request)", cur_cycle); endrule - // ---------------------------------------------------------------- + // ================================================================ // INTERFACE - interface axi4_S = xactor_from_M.axi_side; - interface axi4_M = xactor_to_S .axi_side; - method Action m_enable (Bool enabled); if (enabled && (! rg_enabled) && (verbosity != 0)) - $display ("%0d: %m: AXI4 ENABLING", cur_cycle); + $display ("%0d: AXI4 ENABLING", cur_cycle); else if ((! enabled) && rg_enabled && (verbosity != 0)) - $display ("%0d: %m: AXI4 DISABLING", cur_cycle); + $display ("%0d: AXI4 DISABLING", cur_cycle); rg_enabled <= enabled; endmethod diff --git a/Libraries/AMBA_Fabrics/AXI4/AXI4_Mem_Model.bsv b/Libraries/AMBA_Fabrics/AXI4/AXI4_Mem_Model.bsv index 43c0914..6630d35 100644 --- a/Libraries/AMBA_Fabrics/AXI4/AXI4_Mem_Model.bsv +++ b/Libraries/AMBA_Fabrics/AXI4/AXI4_Mem_Model.bsv @@ -1,13 +1,13 @@ -// Copyright (c) 2019 Bluespec, Inc. All Rights Reserved. +// Copyright (c) 2019-2024 Bluespec, Inc. All Rights Reserved. // Author: Rishiyur S. Nikhil -// + // SPDX-License-Identifier: BSD-3-Clause package AXI4_Mem_Model; -// ================================================================ +// **************************************************************** // A memory-model to be used as an S on an AXI4 bus. -// Only partical functionality; will be gradually improved over time. +// Only partial functionality; to be gradually improved over time. // Current status: // Address and Data bus widths: 64b // Bursts: 'fixed' and 'incr' only @@ -15,10 +15,9 @@ package AXI4_Mem_Model; // Strobes: Not yet handled // memory size: See 'mem_size_word64' definition below -// ================================================================ +// **************************************************************** // Exports -export AXI4_Mem_Model_IFC (..); export mkAXI4_Mem_Model; // ================================================================ @@ -30,184 +29,296 @@ import GetPut :: *; import ClientServer :: *; // ---------------- -// BSV additional libs +// Bluespec misc. libs -import Cur_Cycle :: *; import GetPut_Aux :: *; import Semi_FIFOF :: *; // ================================================================ // Project imports -import AXI4_Types :: *; - -// ================================================================ -// INTERFACE +import AXI4_Types :: *; -interface AXI4_Mem_Model_IFC #(numeric type wd_id, - numeric type wd_addr, - numeric type wd_data, - numeric type wd_user); +// **************************************************************** +// Simulation verbosity during simulation on stdout for this package (edit as desired) +// 0: quiet +// 1: show transactions, brief +// 2: show transactions, full AXI4 structs - method Action init (Bit #(wd_addr) addr_map_base, Bit #(wd_addr) addr_map_lim); +Integer verbosity = 0; - interface AXI4_S_IFC #(wd_id, wd_addr, wd_data, wd_user) s_ifc; +// **************************************************************** -endinterface +typedef enum { STATE_INIT_0, STATE_INIT_1, STATE_RUNNING } State +deriving (Bits, Eq, FShow); -// ================================================================ +// **************************************************************** // IMPLEMENTATION -Integer mem_size_word64 = 'h100_0000; // 16M x 64b words = 128MiB - -function Bool fn_addr_ok (Bit #(64) base, Bit #(64) lim, Bit #(64) addr, AXI4_Size size); - let aligned = fn_addr_is_aligned (addr, size); - let in_range = ((base <= addr) && (addr < lim)); - return (aligned && in_range); -endfunction - -// ---------------- - -module mkAXI4_Mem_Model (AXI4_Mem_Model_IFC #(wd_id, wd_addr, wd_data, wd_user)) - provisos (NumAlias #(wd_addr, 64), - NumAlias #(wd_data, 64)); +// "Memory words" (MW) are same bitwidth as AXI4 data bus (wd_data) + +module mkAXI4_Mem_Model #(Integer id, // Use unique ids for each instance + Bit #(wd_addr) addr_base, // byte addr + Bit #(wd_addr) addr_lim, // byte addr + Bool init_zero, + Bool init_with_memhex, + String memhex_filename, + AXI4_M_IFC #(wd_id, wd_addr, wd_data, wd_user) ifc_M) + (Empty) + provisos (Div #(wd_data, 8, wd_data_B), // see comments on provisos below + Log #(wd_data_B, wd_ix_B_in_MW), + Add #(wd_addrMW, wd_ix_B_in_MW, wd_addr), + Add #(_, 8, wd_addrMW), + Mul #(wd_data_B, 8, wd_data)); - // 0 = quiet; 1 = show mem transactions - Integer verbosity = 1; + // ================================================================ + // Derived constants (some are computed in provisos above) + + // See provisos above for computation of these numeric types: + // wd_data_B: data width in bytes + // wd_ix_B_in_MW: bitwidth of index of a byte in a memword + // wd_addrMW: bitwidth of address of a memword + + // Integer values thereof + Integer i_wd_data_B = valueOf (wd_data_B); + Integer i_wd_ix_B_in_MW = valueOf (wd_ix_B_in_MW); + + // memword addr of first memword + Bit #(wd_addrMW) addrMW_base = truncate (addr_base >> i_wd_ix_B_in_MW); + + // Byte address of byte 0 in first memword + Bit #(wd_addr) addr_base_0 = zeroExtend (addrMW_base) << i_wd_ix_B_in_MW; + + // Number of mem words + Bit #(wd_addrMW) num_MW = truncate ((addr_lim - addr_base_0 - 1 + fromInteger (i_wd_data_B)) + >> i_wd_ix_B_in_MW); + // memword addr just beyond the last memword + Bit #(wd_addrMW) addrMW_lim = addrMW_base + num_MW; + + // Memory is modeled as a RegFile where each word is full AXI4 data width (wd_data) + // and indexed by memword addrs + RegFile #(Bit #(wd_addrMW), Bit #(wd_data)) + regfile <- (init_with_memhex + ? mkRegFileLoad (memhex_filename, addrMW_base, addrMW_lim - 1) + : mkRegFile ( addrMW_base, addrMW_lim - 1)); + + Reg #(State) rg_state <- mkReg (STATE_INIT_0); + + // ---------------- + // Check well-formedness of an AXI4 request + + function ActionValue #(Bool) fav_is_well_formed (Bit #(wd_addr) addr, + Bit #(wd_addrMW) addrMW, + AXI4_Len axlen, + AXI4_Size axsize, + AXI4_Burst axburst); + actionvalue + // Check axsize legal for data bus width + Bool ok = True; + Bit #(11) size_b = { fv_AXI4_Size_to_num_bytes (axsize), 3'h0 }; + if (size_b > fromInteger (valueOf (wd_data))) begin + $display ("AXI4_Mem_Model[%0d]: ERROR: axsize > wd_data", id); + $display (" axsize: %0h (= %d bits)", axsize, size_b); + $display (" wd_data: %0d", valueOf (wd_data)); + ok = False; + end + + // Check address is not below addr_base + if (addr < addr_base) begin + $display ("AXI4_Mem_Model[%0d]: ERROR: addr < addr_base", id); + $display (" addr: 0x%0h", addr); + $display (" addr_base: 0x%0h", addr_base); + ok = False; + end + + // Check address is not beyond last memory word + if (addrMW >= addrMW_lim) begin + $display ("AXI4_Mem_Model[%0d]: ERROR: access beyond mem limit", id); + $display (" addr_lim: 0x%0h", addr_lim); + ok = False; + end + + // Check that burst mode is supported + if (axburst == axburst_wrap) begin + $display ("AXI4_Mem_Model[%0d]: ERROR: axburst = wrap; not supported yet.", id); + ok = False; + end + return ok; + endactionvalue + endfunction + + // **************************************************************** + // BEHAVIOR + + rule rl_init (rg_state == STATE_INIT_0); + $display ("================================"); + $display ("AXI4_Mem_Model[%0d]: initialization", id); + + if (addr_base >= addr_lim) begin + $display (" ERROR: init: addr_base >= addr_lim", id); + $display (" addr_base 0x%0h", addr_base); + $display (" addr_lim 0x%0h", addr_lim); + $finish (1); + end - Reg #(Bool) rg_initialized <- mkReg (False); + // Check legal wd_data (8, 16, 32, 64, 128, 256, 512, or 1024 bits) + // i.e., exactly one bit should be set in wd[10:3] + Bit #(11) wd = fromInteger (valueOf (wd_data)); // checks wd_data <= 11'h7FF + if ((countOnes (wd) != 1) || ((wd & 'b111) != 0)) begin + $display (" ERROR: wd_addr should be 8,16,32,...,1024", id); + $display (" addr_base 0x%0h", addr_base); + $display (" addr_lim 0x%0h", addr_lim); + $finish (1); + end - Reg #(Bit #(wd_addr)) rg_addr_map_base <- mkRegU; - Reg #(Bit #(wd_addr)) rg_addr_map_lim <- mkRegU; + $display (" addr_base: 0x%0h addr_lim: 0x%0h (byte addrs)", + addr_base, addr_lim); + $display (" addrMW_base:0x%0h addrMW_lim:0x%0h (word addrs)", + addrMW_base, addrMW_lim); + $display (" AXI4 params: wd_id:%0d wd_addr:%0d wd_data:%0d wd_user:%0d", + valueOf (wd_id), valueOf (wd_addr), + valueOf (wd_data), valueOf (wd_user)); + $display (" Memory contains %0d words, each wd_data bits (%0d bytes) wide", + num_MW, i_wd_data_B); + + if (init_zero) begin + $display (" Zeroing memory"); + rg_state <= STATE_INIT_1; + end + else begin + if (init_with_memhex) + $display (" Loading from memhex file %s: ", memhex_filename); + else + $display (" Memory contents not initialized"); + rg_state <= STATE_RUNNING; + end + $display ("================================"); + endrule - AXI4_S_Xactor_IFC #(wd_id, wd_addr, wd_data, wd_user) xactor <- mkAXI4_S_Xactor; + // Iterate through memory, writing zeroes + Reg #(Bit #(wd_addrMW)) rg_addrMW <- mkReg (addrMW_base); - RegFile #(Bit #(wd_addr), Bit #(wd_data)) rf <- mkRegFile (0, fromInteger (mem_size_word64)); + rule rl_init_mem (rg_state == STATE_INIT_1); + regfile.upd (rg_addrMW, 0); + if (rg_addrMW != (addrMW_lim - 1)) + rg_addrMW <= rg_addrMW + 1; + else begin + $display ("AXI4_Mem_Model[%0d]: zero'd memory", id); + rg_state <= STATE_RUNNING; + end + endrule // ================================================================ // Read requests - // TODO: does a bad addr return 'burst-len' err responses or just 1? + // TODO: on a bad addr (or other error), does AXI4 specs say to return 'burst-len' + // err responses or just one? - Reg #(Bit #(8)) rg_rd_beat <- mkReg (0); + Reg #(Bit #(8)) rg_R_beat <- mkReg (0); // Recv request on RD_ADDR bus // Send burst responses on RD_DATA bus - rule rl_read (rg_initialized); - let rd_addr = xactor.o_rd_addr.first; - let rf_index = ((rd_addr.araddr - rg_addr_map_base) >> 3); - if (rd_addr.arburst == axburst_incr) - rf_index = rf_index + zeroExtend (rg_rd_beat); - let last = (rg_rd_beat == rd_addr.arlen); + rule rl_AR (rg_state == STATE_RUNNING); + let ar = ifc_M.o_AR.first; + + Bit #(wd_addrMW) addrMW = truncateLSB (ar.araddr); + if (ar.arburst == axburst_incr) + addrMW = addrMW + zeroExtend (rg_R_beat); + + Bool ok <- fav_is_well_formed (ar.araddr, addrMW, ar.arlen, ar.arsize, ar.arburst); + + if (! ok) begin + $display ("AXI4_Mem_Model[%0d]: ERROR: AR is not well-formed", id); + $display (" ", fshow_AR (ar)); + end - let addr_ok = fn_addr_ok (rg_addr_map_base, rg_addr_map_lim, rd_addr.araddr, rd_addr.arsize); + let last = (rg_R_beat == ar.arlen); - let data = (addr_ok ? rf.sub (rf_index) : 0); + let data = (ok ? regfile.sub (addrMW) : 0); - AXI4_Rd_Data #(wd_id, wd_data, wd_user) - rd_data = AXI4_Rd_Data {rid: rd_addr.arid, - rdata: data, - rresp: (addr_ok ? axi4_resp_okay : axi4_resp_slverr), - rlast: last, - ruser: rd_addr.aruser}; - xactor.i_rd_data.enq (rd_data); + AXI4_R #(wd_id, wd_data, wd_user) + r = AXI4_R {rid: ar.arid, + rdata: data, + rresp: (ok ? axi4_resp_okay : axi4_resp_slverr), + rlast: last, + ruser: ar.aruser}; + ifc_M.i_R.enq (r); if (last) begin - xactor.o_rd_addr.deq; - rg_rd_beat <= 0; + ifc_M.o_AR.deq; + rg_R_beat <= 0; end else - rg_rd_beat <= rg_rd_beat + 1; + rg_R_beat <= rg_R_beat + 1; if (verbosity != 0) begin - $write ("%0d: %m.rl_read: ", cur_cycle); - $write (fshow_Rd_Addr (rd_addr)); - $write (fshow_Rd_Data (rd_data)); - if (addr_ok) - $display (" beat %0d rf_index 0x%0h", rg_rd_beat, rf_index); - else - $display (" beat 0x%0h BAD ADDR", rg_rd_beat); + $display ("AXI4_Mem_Model[%0d]: rl_AR", id); + $display (" ", fshow_AR (ar)); + $display (" ", fshow_R (r)); + $display (" memword addr 0x%0h beat %0d ", addrMW, rg_R_beat); end endrule // ================================================================ // Write requests - Reg #(Bit #(8)) rg_wr_beat <- mkReg (0); + Reg #(Bit #(8)) rg_W_beat <- mkReg (0); - // Recv request on WR_ADDR bus and burst data on WR_DATA bus, - // send final response on WR_RESP bus - rule rl_write (rg_initialized); - let wr_addr = xactor.o_wr_addr.first; - let wr_data <- pop_o (xactor.o_wr_data); - let rf_index = ((wr_addr.awaddr - rg_addr_map_base) >> 3); - if (wr_addr.awburst == axburst_incr) - rf_index = rf_index + zeroExtend (rg_wr_beat); - let last = (rg_wr_beat == wr_addr.awlen); + // Recv request on AW bus and burst data on W bus, + // send final response on B bus + rule rl_AW_W (rg_state == STATE_RUNNING); + let aw = ifc_M.o_AW.first; + let w <- pop_o (ifc_M.o_W); - let addr_ok = fn_addr_ok (rg_addr_map_base, rg_addr_map_lim, wr_addr.awaddr, wr_addr.awsize); + Bit #(wd_addrMW) addrMW = truncateLSB (aw.awaddr); + if (aw.awburst == axburst_incr) + addrMW = addrMW + zeroExtend (rg_W_beat); - if (addr_ok) - rf.upd (rf_index, wr_data.wdata); + Bool ok <- fav_is_well_formed (aw.awaddr, addrMW, aw.awlen, aw.awsize, aw.awburst); - if (verbosity != 0) begin - $write ("%0d: %m.rl_write: ", cur_cycle); - $write (fshow_Wr_Data (wr_data)); - $write (" ", fshow_Wr_Addr (wr_addr)); - if (addr_ok) - $display (" beat %0d rf_index %0h", rg_wr_beat, rf_index); - else - $display (" beat %0d BAD ADDR", rg_wr_beat); + if (! ok) begin + $display ("AXI4_Mem_Model[%0d]: ERROR: AW is not well-formed", id); + $display (" ", fshow_AW (aw)); + end + + let last = (rg_W_beat == aw.awlen); + + Bit #(wd_data) mask = fn_strb_to_bitmask (w.wstrb); + + // Read-modify-write the memword using AXI4 memword and strobe + if (ok) begin + let old_mw = regfile.sub (addrMW); + let new_mw = (old_mw & (~ mask)) | (w.wdata & mask); + regfile.upd (addrMW, new_mw); end if (last) begin - AXI4_Wr_Resp #(wd_id, wd_user) wr_resp = ?; - wr_resp = AXI4_Wr_Resp {bid: wr_addr.awid, - bresp: (addr_ok ? axi4_resp_okay : axi4_resp_slverr), - buser: wr_addr.awuser}; - xactor.i_wr_resp.enq (wr_resp); - xactor.o_wr_addr.deq; - rg_wr_beat <= 0; + AXI4_B #(wd_id, wd_user) b = AXI4_B {bid: aw.awid, + bresp: (ok ? axi4_resp_okay + : axi4_resp_slverr), + buser: aw.awuser}; + ifc_M.i_B.enq (b); + ifc_M.o_AW.deq; + rg_W_beat <= 0; if (verbosity != 0) - $display (" ", fshow_Wr_Resp (wr_resp)); + $display (" ", fshow_B (b)); end else - rg_wr_beat <= rg_wr_beat + 1; + rg_W_beat <= rg_W_beat + 1; + + if (verbosity != 0) begin + $write ("AXI4_Mem_Model[%0d]: rl_AW_W", id); + $display (" ", fshow_AW (aw)); + $display (" ", fshow_W (w)); + $display (" memword addr 0x%0h beat %0d ", addrMW, rg_W_beat); + end endrule - // ================================================================ + // **************************************************************** // INTERFACE - method Action init (Bit #(wd_addr) addr_map_base, Bit #(wd_addr) addr_map_lim); - if (addr_map_base [2:0] != 3'b0) - $display ("%0d: %m.init: ERROR: unaligned addr_map_base 0x%0h", cur_cycle, addr_map_base); - else if (addr_map_lim [2:0] != 3'b0) - $display ("%0d: %m.init: ERROR: unaligned addr_map_lim 0x%0h", cur_cycle, addr_map_lim); - else if (addr_map_lim <= addr_map_base) - $display ("%0d: %m.init: ERROR: addr_map_base 0x%0h > addr_map_lim 0x%0h", - cur_cycle, - addr_map_base, - addr_map_lim); - else if ((addr_map_lim - addr_map_base) > fromInteger (mem_size_word64 * 8)) - $display ("%0d: %m.init: ERROR: mem size (base 0x%0h, lim 0x%0h) > max (0x%0h)", - cur_cycle, - addr_map_base, - addr_map_lim, - fromInteger (mem_size_word64 * 8)); - else begin - xactor.reset; - rg_addr_map_base <= addr_map_base; - rg_addr_map_lim <= addr_map_lim; - rg_initialized <= True; - $display ("%0d: %m.init: addr_map_base 0x%0h, addr_map_lim 0x%0h", - cur_cycle, - addr_map_base, - addr_map_lim); - end - endmethod - - interface s_ifc = xactor.axi_side; + // Empty endmodule -// ================================================================ +// **************************************************************** endpackage diff --git a/Libraries/AMBA_Fabrics/AXI4/AXI4_Types.bsv b/Libraries/AMBA_Fabrics/AXI4/AXI4_Types.bsv index f9b2ca3..536c6a3 100644 --- a/Libraries/AMBA_Fabrics/AXI4/AXI4_Types.bsv +++ b/Libraries/AMBA_Fabrics/AXI4/AXI4_Types.bsv @@ -1,12 +1,14 @@ -// Copyright (c) 2019 Bluespec, Inc. All Rights Reserved -// +// Copyright (c) 2019-2023 Bluespec, Inc. All Rights Reserved +// Copyright (c) 2024 Rishiyur S. Nikhil. + // SPDX-License-Identifier: BSD-3-Clause package AXI4_Types; // ================================================================ // Facilities for ARM AXI4, consisting of 5 independent channels: -// Write Address, Write Data, Write Response, Read Address and Read Data +// AW (write address), W (write data), B (write response) +// AR (read address), R (read data) // Ref: ARM document: // AMBA AXI and ACE Protocol Specification @@ -15,27 +17,25 @@ package AXI4_Types; // ARM IHI 0022E (ID022613) // Issue E, 22 Feb 2013 -// ================================================================ +// **************************************************************** // BSV library imports import FIFOF :: *; import Connectable :: *; +import Vector :: *; // ---------------- -// BSV additional libs +// Bluespec misc. libs import Semi_FIFOF :: *; import EdgeFIFOFs :: *; // **************************************************************** // **************************************************************** -// Section: RTL-level interfaces +// Section: Basic bus and bus-field types // **************************************************************** // **************************************************************** -// ================================================================ -// Fixed-width AXI4 buses - // ---------------- // AxLen: burst length (# of transfers in a transaction) // Burst_Length = AxLEN[7:0] + 1 (1..256) @@ -58,7 +58,17 @@ AXI4_Size axsize_64 = 3'b_110; AXI4_Size axsize_128 = 3'b_111; function Bit #(8) fv_AXI4_Size_to_num_bytes (AXI4_Size axi4_size); - return (1 << axi4_size); + return (case (axi4_size) + axsize_1: 1; + axsize_2: 2; + axsize_4: 4; + axsize_8: 8; + axsize_16: 16; + axsize_32: 32; + axsize_64: 64; + axsize_128: 128; + default: 0; // Bogus + endcase); endfunction function AXI4_Size fv_num_bytes_to_AXI4_Size (Bit #(8) num_bytes); @@ -75,6 +85,17 @@ function AXI4_Size fv_num_bytes_to_AXI4_Size (Bit #(8) num_bytes); endcase); endfunction +function Bool fn_addr_is_aligned (Bit #(wd_addr) addr, AXI4_Size size); + return ( (size == axsize_1) + || ((size == axsize_2) && (addr [0] == 1'b0)) + || ((size == axsize_4) && (addr [1:0] == 2'b0)) + || ((size == axsize_8) && (addr [2:0] == 3'b0)) + || ((size == axsize_16) && (addr [3:0] == 4'b0)) + || ((size == axsize_32) && (addr [4:0] == 5'b0)) + || ((size == axsize_64) && (addr [5:0] == 6'b0)) + || ((size == axsize_128) && (addr [6:0] == 7'b0))); +endfunction + // ---------------- // AxBURST Burst type @@ -154,467 +175,26 @@ AXI4_Resp axi4_resp_exokay = 2'b_01; AXI4_Resp axi4_resp_slverr = 2'b_10; AXI4_Resp axi4_resp_decerr = 2'b_11; -// ================================================================ -// Function to check address-alignment - -function Bool fn_addr_is_aligned (Bit #(wd_addr) addr, AXI4_Size size); - return ( (size == axsize_1) - || ((size == axsize_2) && (addr [0] == 1'b0)) - || ((size == axsize_4) && (addr [1:0] == 2'b0)) - || ((size == axsize_8) && (addr [2:0] == 3'b0)) - || ((size == axsize_16) && (addr [3:0] == 4'b0)) - || ((size == axsize_32) && (addr [4:0] == 5'b0)) - || ((size == axsize_64) && (addr [5:0] == 6'b0)) - || ((size == axsize_128) && (addr [6:0] == 7'b0))); -endfunction - -// ================================================================ -// These are the signal-level interfaces for an AXI4 M. -// The (*..*) attributes ensure that when bsc compiles this to Verilog, -// we get exactly the signals specified in the ARM spec. - -interface AXI4_M_IFC #(numeric type wd_id, - numeric type wd_addr, - numeric type wd_data, - numeric type wd_user); - // ---------------- - // Wr Addr channel - (* always_ready, result="awvalid" *) method Bool m_awvalid; // out - - (* always_ready, result="awid" *) method Bit #(wd_id) m_awid; // out - (* always_ready, result="awaddr" *) method Bit #(wd_addr) m_awaddr; // out - (* always_ready, result="awlen" *) method Bit #(8) m_awlen; // out - (* always_ready, result="awsize" *) method AXI4_Size m_awsize; // out - (* always_ready, result="awburst" *) method Bit #(2) m_awburst; // out - (* always_ready, result="awlock" *) method Bit #(1) m_awlock; // out - (* always_ready, result="awcache" *) method Bit #(4) m_awcache; // out - (* always_ready, result="awprot" *) method Bit #(3) m_awprot; // out - (* always_ready, result="awqos" *) method Bit #(4) m_awqos; // out - (* always_ready, result="awregion" *) method Bit #(4) m_awregion; // out - (* always_ready, result="awuser" *) method Bit #(wd_user) m_awuser; // out - - (* always_ready, always_enabled, prefix="" *) - method Action m_awready ((* port="awready" *) Bool awready); // in - - // ---------------- - // Wr Data channel - (* always_ready, result="wvalid" *) method Bool m_wvalid; // out - - (* always_ready, result="wdata" *) method Bit #(wd_data) m_wdata; // out - (* always_ready, result="wstrb" *) method Bit #(TDiv #(wd_data, 8)) m_wstrb; // out - (* always_ready, result="wlast" *) method Bool m_wlast; // out - (* always_ready, result="wuser" *) method Bit #(wd_user) m_wuser; // out - - (* always_ready, always_enabled, prefix = "" *) - method Action m_wready ((* port="wready" *) Bool wready); // in - - // ---------------- - // Wr Response channel - (* always_ready, always_enabled, prefix = "" *) - method Action m_bvalid ((* port="bvalid" *) Bool bvalid, // in - (* port="bid" *) Bit #(wd_id) bid, // in - (* port="bresp" *) Bit #(2) bresp, // in - (* port="buser" *) Bit #(wd_user) buser); // in - - (* always_ready, prefix = "", result="bready" *) - method Bool m_bready; // out - - // ---------------- - // Rd Addr channel - (* always_ready, result="arvalid" *) method Bool m_arvalid; // out - - (* always_ready, result="arid" *) method Bit #(wd_id) m_arid; // out - (* always_ready, result="araddr" *) method Bit #(wd_addr) m_araddr; // out - (* always_ready, result="arlen" *) method Bit #(8) m_arlen; // out - (* always_ready, result="arsize" *) method AXI4_Size m_arsize; // out - (* always_ready, result="arburst" *) method Bit #(2) m_arburst; // out - (* always_ready, result="arlock" *) method Bit #(1) m_arlock; // out - (* always_ready, result="arcache" *) method Bit #(4) m_arcache; // out - (* always_ready, result="arprot" *) method Bit #(3) m_arprot; // out - (* always_ready, result="arqos" *) method Bit #(4) m_arqos; // out - (* always_ready, result="arregion" *) method Bit #(4) m_arregion; // out - (* always_ready, result="aruser" *) method Bit #(wd_user) m_aruser; // out - - (* always_ready, always_enabled, prefix="" *) - method Action m_arready ((* port="arready" *) Bool arready); // in - - // ---------------- - // Rd Data channel - (* always_ready, always_enabled, prefix = "" *) - method Action m_rvalid ((* port="rvalid" *) Bool rvalid, // in - (* port="rid" *) Bit #(wd_id) rid, // in - (* port="rdata" *) Bit #(wd_data) rdata, // in - (* port="rresp" *) Bit #(2) rresp, // in - (* port="rlast" *) Bool rlast, // in - (* port="ruser" *) Bit #(wd_user) ruser); // in - - (* always_ready, result="rready" *) - method Bool m_rready; // out -endinterface: AXI4_M_IFC - -// ================================================================ -// These are the signal-level interfaces for an AXI4-Lite S. -// The (*..*) attributes ensure that when bsc compiles this to Verilog, -// we get exactly the signals specified in the ARM spec. - -interface AXI4_S_IFC #(numeric type wd_id, - numeric type wd_addr, - numeric type wd_data, - numeric type wd_user); - // Wr Addr channel - (* always_ready, always_enabled, prefix = "" *) - method Action m_awvalid ((* port="awvalid" *) Bool awvalid, // in - (* port="awid" *) Bit #(wd_id) awid, // in - (* port="awaddr" *) Bit #(wd_addr) awaddr, // in - (* port="awlen" *) Bit #(8) awlen, // in - (* port="awsize" *) AXI4_Size awsize, // in - (* port="awburst" *) Bit #(2) awburst, // in - (* port="awlock" *) Bit #(1) awlock, // in - (* port="awcache" *) Bit #(4) awcache, // in - (* port="awprot" *) Bit #(3) awprot, // in - (* port="awqos" *) Bit #(4) awqos, // in - (* port="awregion" *) Bit #(4) awregion, // in - (* port="awuser" *) Bit #(wd_user) awuser); // in - (* always_ready, result="awready" *) - method Bool m_awready; // out - - // Wr Data channel - (* always_ready, always_enabled, prefix = "" *) - method Action m_wvalid ((* port="wvalid" *) Bool wvalid, // in - (* port="wdata" *) Bit #(wd_data) wdata, // in - (* port="wstrb" *) Bit #(TDiv #(wd_data,8)) wstrb, // in - (* port="wlast" *) Bool wlast, // in - (* port="wuser" *) Bit #(wd_user) wuser); // in - (* always_ready, result="wready" *) - method Bool m_wready; // out - - // Wr Response channel - (* always_ready, result="bvalid" *) method Bool m_bvalid; // out - (* always_ready, result="bid" *) method Bit #(wd_id) m_bid; // out - (* always_ready, result="bresp" *) method Bit #(2) m_bresp; // out - (* always_ready, result="buser" *) method Bit #(wd_user) m_buser; // out - (* always_ready, always_enabled, prefix="" *) - method Action m_bready ((* port="bready" *) Bool bready); // in - - // Rd Addr channel - (* always_ready, always_enabled, prefix = "" *) - method Action m_arvalid ((* port="arvalid" *) Bool arvalid, // in - (* port="arid" *) Bit #(wd_id) arid, // in - (* port="araddr" *) Bit #(wd_addr) araddr, // in - (* port="arlen" *) Bit #(8) arlen, // in - (* port="arsize" *) AXI4_Size arsize, // in - (* port="arburst" *) Bit #(2) arburst, // in - (* port="arlock" *) Bit #(1) arlock, // in - (* port="arcache" *) Bit #(4) arcache, // in - (* port="arprot" *) Bit #(3) arprot, // in - (* port="arqos" *) Bit #(4) arqos, // in - (* port="arregion" *) Bit #(4) arregion, // in - (* port="aruser" *) Bit #(wd_user) aruser); // in - (* always_ready, result="arready" *) - method Bool m_arready; // out - - // Rd Data channel - (* always_ready, result="rvalid" *) method Bool m_rvalid; // out - (* always_ready, result="rid" *) method Bit #(wd_id) m_rid; // out - (* always_ready, result="rdata" *) method Bit #(wd_data) m_rdata; // out - (* always_ready, result="rresp" *) method Bit #(2) m_rresp; // out - (* always_ready, result="rlast" *) method Bool m_rlast; // out - (* always_ready, result="ruser" *) method Bit #(wd_user) m_ruser; // out - (* always_ready, always_enabled, prefix="" *) - method Action m_rready ((* port="rready" *) Bool rready); // in -endinterface: AXI4_S_IFC - -// ================================================================ -// Connecting signal-level interfaces - -instance Connectable #(AXI4_M_IFC #(wd_id, wd_addr, wd_data, wd_user), - AXI4_S_IFC #(wd_id, wd_addr, wd_data, wd_user)); - - module mkConnection #(AXI4_M_IFC #(wd_id, wd_addr, wd_data, wd_user) axim, - AXI4_S_IFC #(wd_id, wd_addr, wd_data, wd_user) axis) - (Empty); - - (* fire_when_enabled, no_implicit_conditions *) - rule rl_wr_addr_channel; - axis.m_awvalid (axim.m_awvalid, - axim.m_awid, - axim.m_awaddr, - axim.m_awlen, - axim.m_awsize, - axim.m_awburst, - axim.m_awlock, - axim.m_awcache, - axim.m_awprot, - axim.m_awqos, - axim.m_awregion, - axim.m_awuser); - axim.m_awready (axis.m_awready); - endrule - - (* fire_when_enabled, no_implicit_conditions *) - rule rl_wr_data_channel; - axis.m_wvalid (axim.m_wvalid, - axim.m_wdata, - axim.m_wstrb, - axim.m_wlast, - axim.m_wuser); - axim.m_wready (axis.m_wready); - endrule - - (* fire_when_enabled, no_implicit_conditions *) - rule rl_wr_response_channel; - axim.m_bvalid (axis.m_bvalid, - axis.m_bid, - axis.m_bresp, - axis.m_buser); - axis.m_bready (axim.m_bready); - endrule - - (* fire_when_enabled, no_implicit_conditions *) - rule rl_rd_addr_channel; - axis.m_arvalid (axim.m_arvalid, - axim.m_arid, - axim.m_araddr, - axim.m_arlen, - axim.m_arsize, - axim.m_arburst, - axim.m_arlock, - axim.m_arcache, - axim.m_arprot, - axim.m_arqos, - axim.m_arregion, - axim.m_aruser); - axim.m_arready (axis.m_arready); - endrule - - (* fire_when_enabled, no_implicit_conditions *) - rule rl_rd_data_channel; - axim.m_rvalid (axis.m_rvalid, - axis.m_rid, - axis.m_rdata, - axis.m_rresp, - axis.m_rlast, - axis.m_ruser); - axis.m_rready (axim.m_rready); - endrule - endmodule -endinstance - -// ================================================================ -// AXI4 dummy M: never produces requests, never accepts responses - -AXI4_M_IFC #(wd_id, wd_addr, wd_data, wd_user) - dummy_AXI4_M_ifc = interface AXI4_M_IFC - // Wr Addr channel - method Bool m_awvalid = False; // out - method Bit #(wd_id) m_awid = ?; // out - method Bit #(wd_addr) m_awaddr = ?; // out - method Bit #(8) m_awlen = ?; // out - method AXI4_Size m_awsize = ?; // out - method Bit #(2) m_awburst = ?; // out - method Bit #(1) m_awlock = ?; // out - method Bit #(4) m_awcache = ?; // out - method Bit #(3) m_awprot = ?; // out - method Bit #(4) m_awqos = ?; // out - method Bit #(4) m_awregion = ?; // out - method Bit #(wd_user) m_awuser = ?; // out - method Action m_awready (Bool awready) = noAction; // in - - // Wr Data channel - method Bool m_wvalid = False; // out - method Bit #(wd_data) m_wdata = ?; // out - method Bit #(TDiv #(wd_data, 8)) m_wstrb = ?; // out - method Bool m_wlast = ?; // out - method Bit #(wd_user) m_wuser = ?; // out - - method Action m_wready (Bool wready) = noAction; // in - - // Wr Response channel - method Action m_bvalid (Bool bvalid, // in - Bit #(wd_id) bid, // in - Bit #(2) bresp, // in - Bit #(wd_user) buser); // in - noAction; - endmethod - method Bool m_bready = False; // out - - // Rd Addr channel - method Bool m_arvalid = False; // out - method Bit #(wd_id) m_arid = ?; // out - method Bit #(wd_addr) m_araddr = ?; // out - method Bit #(8) m_arlen = ?; // out - method AXI4_Size m_arsize = ?; // out - method Bit #(2) m_arburst = ?; // out - method Bit #(1) m_arlock = ?; // out - method Bit #(4) m_arcache = ?; // out - method Bit #(3) m_arprot = ?; // out - method Bit #(4) m_arqos = ?; // out - method Bit #(4) m_arregion = ?; // out - method Bit #(wd_user) m_aruser = ?; // out - method Action m_arready (Bool arready) = noAction; // in - - // Rd Data channel - method Action m_rvalid (Bool rvalid, // in - Bit #(wd_id) rid, // in - Bit #(wd_data) rdata, // in - Bit #(2) rresp, // in - Bool rlast, // in - Bit #(wd_user) ruser); // in - noAction; - endmethod - method Bool m_rready = False; // out - endinterface; +// ---------------- +// Expand the AXI4 'wstrb' field into a bit-mask -// ================================================================ -// AXI4 dummy S: never accepts requests, never produces responses - -AXI4_S_IFC #(wd_id, wd_addr, wd_data, wd_user) - dummy_AXI4_S_ifc = interface AXI4_S_IFC - // Wr Addr channel - method Action m_awvalid (Bool awvalid, - Bit #(wd_id) awid, - Bit #(wd_addr) awaddr, - Bit #(8) awlen, - AXI4_Size awsize, - Bit #(2) awburst, - Bit #(1) awlock, - Bit #(4) awcache, - Bit #(3) awprot, - Bit #(4) awqos, - Bit #(4) awregion, - Bit #(wd_user) awuser); - noAction; - endmethod - - method Bool m_awready; - return False; - endmethod - - // Wr Data channel - method Action m_wvalid (Bool wvalid, - Bit #(wd_data) wdata, - Bit #(TDiv #(wd_data, 8)) wstrb, - Bool wlast, - Bit #(wd_user) wuser); - noAction; - endmethod - - method Bool m_wready; - return False; - endmethod - - // Wr Response channel - method Bool m_bvalid; - return False; - endmethod - - method Bit #(wd_id) m_bid; - return ?; - endmethod - - method Bit #(2) m_bresp; - return 0; - endmethod - - method Bit #(wd_user) m_buser; - return ?; - endmethod - - method Action m_bready (Bool bready); - noAction; - endmethod - - // Rd Addr channel - method Action m_arvalid (Bool arvalid, - Bit #(wd_id) arid, - Bit #(wd_addr) araddr, - Bit #(8) arlen, - AXI4_Size arsize, - Bit #(2) arburst, - Bit #(1) arlock, - Bit #(4) arcache, - Bit #(3) arprot, - Bit #(4) arqos, - Bit #(4) arregion, - Bit #(wd_user) aruser); - noAction; - endmethod - - method Bool m_arready; - return False; - endmethod - - // Rd Data channel - method Bool m_rvalid; - return False; - endmethod - - method Bit #(wd_id) m_rid; - return 0; - endmethod - - method Bit #(wd_data) m_rdata; - return 0; - endmethod - - method Bit #(2) m_rresp; - return 0; - endmethod - - method Bool m_rlast; - return True; - endmethod - - method Bit #(wd_user) m_ruser; - return ?; - endmethod - - method Action m_rready (Bool rready); - noAction; - endmethod - endinterface; +function Bit #(wd_data) fn_strb_to_bitmask (Bit #(wd_data_B) strb) + provisos (Mul #(wd_data_B, 8, wd_data)); -// **************************************************************** -// **************************************************************** -// Section: Higher-level FIFO-like interfaces and transactors -// **************************************************************** -// **************************************************************** - -// ================================================================ -// Help function: fn_crg_and_rg_to_FIFOF_I -// In the modules below, we use a crg_full and a rg_data to represent a fifo. -// These functions convert these to FIFOF_I and FIFOF_O interfaces. - -function FIFOF_I #(t) fn_crg_and_rg_to_FIFOF_I (Reg #(Bool) rg_full, Reg #(t) rg_data); - return interface FIFOF_I; - method Action enq (t x) if (! rg_full); - rg_full <= True; - rg_data <= x; - endmethod - method Bool notFull; - return (! rg_full); - endmethod - endinterface; -endfunction + function Bit #(8) fn_bit_to_byte (Integer j); + return ((strb [j] == 1'b0) ? 0 : 'hFF); + endfunction -function FIFOF_O #(t) fn_crg_and_rg_to_FIFOF_O (Reg #(Bool) rg_full, Reg #(t) rg_data); - return interface FIFOF_O; - method t first () if (rg_full); - return rg_data; - endmethod - method Action deq () if (rg_full); - rg_full <= False; - endmethod - method notEmpty; - return rg_full; - endmethod - endinterface; + begin + Vector #(wd_Data_B, Bit #(8)) v_bytes = genWith (fn_bit_to_byte); + return pack (v_bytes); + end endfunction // ================================================================ -// Higher-level types for payloads (rather than just bits) +// AR,R,AW,W,B struct types -// Write Address channel +// AW channel (Write Address) typedef struct { Bit #(wd_id) awid; @@ -628,33 +208,33 @@ typedef struct { Bit #(4) awqos; Bit #(4) awregion; Bit #(wd_user) awuser; - } AXI4_Wr_Addr #(numeric type wd_id, - numeric type wd_addr, - numeric type wd_user) + } AXI4_AW #(numeric type wd_id, + numeric type wd_addr, + numeric type wd_user) deriving (Bits, FShow); -// Write Data channel +// W channel (Write Data) typedef struct { Bit #(wd_data) wdata; Bit #(TDiv #(wd_data, 8)) wstrb; Bool wlast; Bit #(wd_user) wuser; - } AXI4_Wr_Data #(numeric type wd_data, - numeric type wd_user) + } AXI4_W #(numeric type wd_data, + numeric type wd_user) deriving (Bits, FShow); -// Write Response channel +// B channel (Write Response) typedef struct { Bit #(wd_id) bid; Bit #(2) bresp; Bit #(wd_user) buser; - } AXI4_Wr_Resp #(numeric type wd_id, - numeric type wd_user) + } AXI4_B #(numeric type wd_id, + numeric type wd_user) deriving (Bits, FShow); -// Read Address channel +// AR channel (Read Address) typedef struct { Bit #(wd_id) arid; @@ -668,12 +248,12 @@ typedef struct { Bit #(4) arqos; Bit #(4) arregion; Bit #(wd_user) aruser; - } AXI4_Rd_Addr #(numeric type wd_id, - numeric type wd_addr, - numeric type wd_user) + } AXI4_AR #(numeric type wd_id, + numeric type wd_addr, + numeric type wd_user) deriving (Bits, FShow); -// Read Data channel +// R channel (Read Data)) typedef struct { Bit #(wd_id) rid; @@ -681,11 +261,71 @@ typedef struct { Bit #(2) rresp; Bool rlast; Bit #(wd_user) ruser; - } AXI4_Rd_Data #(numeric type wd_id, - numeric type wd_data, - numeric type wd_user) + } AXI4_R #(numeric type wd_id, + numeric type wd_data, + numeric type wd_user) deriving (Bits, FShow); +// ================================================================ +// The following functions change ID (possibly to different width). +// These are used in interconnect fabrics to 'push' extra id bits on +// M->S request traffic and 'pop' those bits in S->M response traffic. +// The extra bits specify to which M a response should be sent. + +function AXI4_AW #(wd_id_out, wd_addr, wd_user) + fn_change_AW_id (AXI4_AW #(wd_id_in, wd_addr, wd_user) aw_in, + Bit #(wd_id_out) awid_out); + let aw_out = AXI4_AW {awid: awid_out, + awaddr: aw_in.awaddr, + awlen: aw_in.awlen, + awsize: aw_in.awsize, + awburst: aw_in.awburst, + awlock: aw_in.awlock, + awcache: aw_in.awcache, + awprot: aw_in.awprot, + awqos: aw_in.awqos, + awregion: aw_in.awregion, + awuser: aw_in.awuser}; + return aw_out; +endfunction + +function AXI4_B #(wd_id_out, wd_user) + fn_change_B_id (AXI4_B #(wd_id_in, wd_user) b_in, + Bit #(wd_id_out) bid_out); + let b_out = AXI4_B {bid: bid_out, + bresp: b_in.bresp, + buser: b_in.buser}; + return b_out; +endfunction + +function AXI4_AR #(wd_id_out, wd_addr, wd_user) + fn_change_AR_id (AXI4_AR #(wd_id_in, wd_addr, wd_user) ar_in, + Bit #(wd_id_out) arid_out); + let ar_out = AXI4_AR {arid: arid_out, + araddr: ar_in.araddr, + arlen: ar_in.arlen, + arsize: ar_in.arsize, + arburst: ar_in.arburst, + arlock: ar_in.arlock, + arcache: ar_in.arcache, + arprot: ar_in.arprot, + arqos: ar_in.arqos, + arregion: ar_in.arregion, + aruser: ar_in.aruser}; + return ar_out; +endfunction + +function AXI4_R #(wd_id_out, wd_data, wd_user) + fn_change_R_id (AXI4_R #(wd_id_in, wd_data, wd_user) r_in, + Bit #(wd_id_out) rid_out); + let r_out = AXI4_R {rid: rid_out, + rdata: r_in.rdata, + rresp: r_in.rresp, + rlast: r_in.rlast, + ruser: r_in.ruser}; + return r_out; +endfunction + // ================================================================ // The following are specialized 'fshow' functions for AXI4 bus // payloads: the most common fields, and more compact. @@ -723,781 +363,184 @@ endfunction // ---------------- -function Fmt fshow_Wr_Addr (AXI4_Wr_Addr #(wd_id, wd_addr, wd_user) x); - Fmt result = ($format ("{awaddr:%0h,", x.awaddr) - + $format ("awlen:%0d", x.awlen) - + $format (",") +function Fmt fshow_AW (AXI4_AW #(wd_id, wd_addr, wd_user) x); + Fmt result = ($format ("AW{id:%0h addr:%0h", x.awid, x.awaddr) + + $format (" len:%0d", x.awlen) + + $format (" ") + fshow_AXI4_Size (x.awsize) - + $format (",") + + $format (" ") + fshow_AXI4_Burst (x.awburst) - + $format ("}")); + + $format (" user:%0h ..}", x.awuser)); return result; endfunction -function Fmt fshow_Wr_Data (AXI4_Wr_Data #(wd_data, wd_user) x); - let result = ($format ("{wdata:%0h,wstrb:%0h", x.wdata, x.wstrb) - + (x.wlast ? $format (",wlast") : $format (",..")) - + $format ("}")); +function Fmt fshow_W (AXI4_W #(wd_data, wd_user) x); + let result = ($format ("W{data:%0h strb:%0h", x.wdata, x.wstrb) + + (x.wlast ? $format (" last") : $format ("")) + + $format (" user:%0h ..}", x.wuser)); return result; endfunction -function Fmt fshow_Wr_Resp (AXI4_Wr_Resp #(wd_id, wd_user) x); - Fmt result = ($format ("{bresp:") +function Fmt fshow_B (AXI4_B #(wd_id, wd_user) x); + Fmt result = ($format ("B{id:%0h resp:", x.bid) + fshow_AXI4_Resp (x.bresp) - + $format ("}")); + + $format (" user:%0h ..}", x.buser)); return result; endfunction -function Fmt fshow_Rd_Addr (AXI4_Rd_Addr #(wd_id, wd_addr, wd_user) x); - Fmt result = ($format ("{araddr:%0h", x.araddr) - + $format (",arlen:%0d", x.arlen) - + $format (",") +function Fmt fshow_AR (AXI4_AR #(wd_id, wd_addr, wd_user) x); + Fmt result = ($format ("AR{id:%0h addr:%0h", x.arid, x.araddr) + + $format (" len:%0d", x.arlen) + + $format (" ") + fshow_AXI4_Size (x.arsize) - + $format (",") + + $format (" ") + fshow_AXI4_Burst (x.arburst) - + $format ("}")); + + $format (" user:%0h ..}", x.aruser)); return result; endfunction -function Fmt fshow_Rd_Data (AXI4_Rd_Data #(wd_id, wd_data, wd_user) x); - Fmt result = ($format ("{rresp:") +function Fmt fshow_R (AXI4_R #(wd_id, wd_data, wd_user) x); + Fmt result = ($format ("R{id%0h resp:", x.rid) + fshow_AXI4_Resp (x.rresp) - + $format (",rdata:%0h", x.rdata) - + (x.rlast ? $format (",rlast") : $format (",..")) - + $format ("}")); + + $format (" data:%0h", x.rdata) + + (x.rlast ? $format (" last") : $format ("")) + + $format (" user:%0h ..}", x.ruser)); return result; endfunction +// **************************************************************** +// **************************************************************** +// Section: Higher-level FIFO-like interfaces and transactors +// **************************************************************** +// **************************************************************** + // ================================================================ -// AXI4 buffer +// AXI4 interfaces with BSV FIFO sub-interfaces for each channel +// (instead of RTL ready/valid signaling) // ---------------- -// Server-side interface accepts requests and yields responses +// M interface with FIFOs -interface AXI4_Server_IFC #(numeric type wd_id, - numeric type wd_addr, - numeric type wd_data, - numeric type wd_user); +interface AXI4_M_IFC #(numeric type wd_id, + numeric type wd_addr, + numeric type wd_data, + numeric type wd_user); - interface FIFOF_I #(AXI4_Wr_Addr #(wd_id, wd_addr, wd_user)) i_wr_addr; - interface FIFOF_I #(AXI4_Wr_Data #(wd_data, wd_user)) i_wr_data; - interface FIFOF_O #(AXI4_Wr_Resp #(wd_id, wd_user)) o_wr_resp; + interface FIFOF_O #(AXI4_AW #(wd_id, wd_addr, wd_user)) o_AW; + interface FIFOF_O #(AXI4_W #(wd_data, wd_user)) o_W; + interface FIFOF_I #(AXI4_B #(wd_id, wd_user)) i_B; - interface FIFOF_I #(AXI4_Rd_Addr #(wd_id, wd_addr, wd_user)) i_rd_addr; - interface FIFOF_O #(AXI4_Rd_Data #(wd_id, wd_data, wd_user)) o_rd_data; + interface FIFOF_O #(AXI4_AR #(wd_id, wd_addr, wd_user)) o_AR; + interface FIFOF_I #(AXI4_R #(wd_id, wd_data, wd_user)) i_R; endinterface // ---------------- -// Client-side interface yields requests and accepts responses +// S interface with FIFOs -interface AXI4_Client_IFC #(numeric type wd_id, - numeric type wd_addr, - numeric type wd_data, - numeric type wd_user); +interface AXI4_S_IFC #(numeric type wd_id, + numeric type wd_addr, + numeric type wd_data, + numeric type wd_user); - interface FIFOF_O #(AXI4_Wr_Addr #(wd_id, wd_addr, wd_user)) o_wr_addr; - interface FIFOF_O #(AXI4_Wr_Data #(wd_data, wd_user)) o_wr_data; - interface FIFOF_I #(AXI4_Wr_Resp #(wd_id, wd_user)) i_wr_resp; + interface FIFOF_I #(AXI4_AW #(wd_id, wd_addr, wd_user)) i_AW; + interface FIFOF_I #(AXI4_W #(wd_data, wd_user)) i_W; + interface FIFOF_O #(AXI4_B #(wd_id, wd_user)) o_B; - interface FIFOF_O #(AXI4_Rd_Addr #(wd_id, wd_addr, wd_user)) o_rd_addr; - interface FIFOF_I #(AXI4_Rd_Data #(wd_id, wd_data, wd_user)) i_rd_data; + interface FIFOF_I #(AXI4_AR #(wd_id, wd_addr, wd_user)) i_AR; + interface FIFOF_O #(AXI4_R #(wd_id, wd_data, wd_user)) o_R; endinterface // ---------------- -// A Buffer has a server-side and a client-side, and a reset +// Connecting AXI4_M_IFC and AXI4_S_IFC + +instance Connectable #(AXI4_M_IFC #(wd_id, wd_addr, wd_data, wd_user), + AXI4_S_IFC #(wd_id, wd_addr, wd_data, wd_user)); + + module mkConnection #(AXI4_M_IFC #(wd_id, wd_addr, wd_data, wd_user) m, + AXI4_S_IFC #(wd_id, wd_addr, wd_data, wd_user) s) (Empty); + mkConnection (m.o_AW, s.i_AW); + mkConnection (m.o_W, s.i_W); + mkConnection (m.i_B, s.o_B); + mkConnection (m.o_AR, s.i_AR); + mkConnection (m.i_R, s.o_R); + endmodule +endinstance + +// ================================================================ +// Interface of an AXI4 buffer with FIFO-like interfaces on both sides interface AXI4_Buffer_IFC #(numeric type wd_id, numeric type wd_addr, numeric type wd_data, numeric type wd_user); - method Action reset; - interface AXI4_Server_IFC #(wd_id, wd_addr, wd_data, wd_user) server_side; - interface AXI4_Client_IFC #(wd_id, wd_addr, wd_data, wd_user) client_side; + // Facing upstream + interface AXI4_S_IFC #(wd_id, wd_addr, wd_data, wd_user) ifc_S; + // Facing downstream + interface AXI4_M_IFC #(wd_id, wd_addr, wd_data, wd_user) ifc_M; endinterface // ---------------------------------------------------------------- +// The following two modules differ only in using mkFIFOF or +// mkM_EdgeFIFOF internally. +// Note: we can't write a single module parameterized by the internal +// FIFOs because mkFIFOF is instantiated at different types. module mkAXI4_Buffer (AXI4_Buffer_IFC #(wd_id, wd_addr, wd_data, wd_user)); - FIFOF #(AXI4_Wr_Addr #(wd_id, wd_addr, wd_user)) f_wr_addr <- mkFIFOF; - FIFOF #(AXI4_Wr_Data #(wd_data, wd_user)) f_wr_data <- mkFIFOF; - FIFOF #(AXI4_Wr_Resp #(wd_id, wd_user)) f_wr_resp <- mkFIFOF; + FIFOF #(AXI4_AW #(wd_id, wd_addr, wd_user)) f_AW <- mkFIFOF; + FIFOF #(AXI4_W #(wd_data, wd_user)) f_W <- mkFIFOF; + FIFOF #(AXI4_B #(wd_id, wd_user)) f_B <- mkFIFOF; - FIFOF #(AXI4_Rd_Addr #(wd_id, wd_addr, wd_user)) f_rd_addr <- mkFIFOF; - FIFOF #(AXI4_Rd_Data #(wd_id, wd_data, wd_user)) f_rd_data <- mkFIFOF; + FIFOF #(AXI4_AR #(wd_id, wd_addr, wd_user)) f_AR <- mkFIFOF; + FIFOF #(AXI4_R #(wd_id, wd_data, wd_user)) f_R <- mkFIFOF; - method Action reset; - f_wr_addr.clear; - f_wr_data.clear; - f_wr_resp.clear; + interface AXI4_M_IFC ifc_M; + interface o_AW = to_FIFOF_O (f_AW); + interface o_W = to_FIFOF_O (f_W); + interface i_B = to_FIFOF_I (f_B); - f_rd_addr.clear; - f_rd_data.clear; - endmethod - - interface AXI4_Server_IFC server_side; - interface i_wr_addr = to_FIFOF_I (f_wr_addr); - interface i_wr_data = to_FIFOF_I (f_wr_data); - interface o_wr_resp = to_FIFOF_O (f_wr_resp); - - interface i_rd_addr = to_FIFOF_I (f_rd_addr); - interface o_rd_data = to_FIFOF_O (f_rd_data); + interface o_AR = to_FIFOF_O (f_AR); + interface i_R = to_FIFOF_I (f_R); endinterface - interface AXI4_Client_IFC client_side; - interface o_wr_addr = to_FIFOF_O (f_wr_addr); - interface o_wr_data = to_FIFOF_O (f_wr_data); - interface i_wr_resp = to_FIFOF_I (f_wr_resp); + interface AXI4_S_IFC ifc_S; + interface i_AW = to_FIFOF_I (f_AW); + interface i_W = to_FIFOF_I (f_W); + interface o_B = to_FIFOF_O (f_B); - interface o_rd_addr = to_FIFOF_O (f_rd_addr); - interface i_rd_data = to_FIFOF_I (f_rd_data); + interface i_AR = to_FIFOF_I (f_AR); + interface o_R = to_FIFOF_O (f_R); endinterface endmodule module mkAXI4_Buffer_2 (AXI4_Buffer_IFC #(wd_id, wd_addr, wd_data, wd_user)); - FIFOF #(AXI4_Wr_Addr #(wd_id, wd_addr, wd_user)) f_wr_addr <- mkM_EdgeFIFOF; - FIFOF #(AXI4_Wr_Data #(wd_data, wd_user)) f_wr_data <- mkM_EdgeFIFOF; - FIFOF #(AXI4_Wr_Resp #(wd_id, wd_user)) f_wr_resp <- mkS_EdgeFIFOF; - - FIFOF #(AXI4_Rd_Addr #(wd_id, wd_addr, wd_user)) f_rd_addr <- mkM_EdgeFIFOF; - FIFOF #(AXI4_Rd_Data #(wd_id, wd_data, wd_user)) f_rd_data <- mkS_EdgeFIFOF; - - method Action reset; - f_wr_addr.clear; - f_wr_data.clear; - f_wr_resp.clear; + FIFOF #(AXI4_AW #(wd_id, wd_addr, wd_user)) f_AW <- mkM_EdgeFIFOF; + FIFOF #(AXI4_W #(wd_data, wd_user)) f_W <- mkM_EdgeFIFOF; + FIFOF #(AXI4_B #(wd_id, wd_user)) f_B <- mkS_EdgeFIFOF; - f_rd_addr.clear; - f_rd_data.clear; - endmethod + FIFOF #(AXI4_AR #(wd_id, wd_addr, wd_user)) f_AR <- mkM_EdgeFIFOF; + FIFOF #(AXI4_R #(wd_id, wd_data, wd_user)) f_R <- mkS_EdgeFIFOF; - interface AXI4_Server_IFC server_side; - interface i_wr_addr = to_FIFOF_I (f_wr_addr); - interface i_wr_data = to_FIFOF_I (f_wr_data); - interface o_wr_resp = to_FIFOF_O (f_wr_resp); + interface AXI4_M_IFC ifc_M; + interface o_AW = to_FIFOF_O (f_AW); + interface o_W = to_FIFOF_O (f_W); + interface i_B = to_FIFOF_I (f_B); - interface i_rd_addr = to_FIFOF_I (f_rd_addr); - interface o_rd_data = to_FIFOF_O (f_rd_data); + interface o_AR = to_FIFOF_O (f_AR); + interface i_R = to_FIFOF_I (f_R); endinterface - interface AXI4_Client_IFC client_side; - interface o_wr_addr = to_FIFOF_O (f_wr_addr); - interface o_wr_data = to_FIFOF_O (f_wr_data); - interface i_wr_resp = to_FIFOF_I (f_wr_resp); + interface AXI4_S_IFC ifc_S; + interface i_AW = to_FIFOF_I (f_AW); + interface i_W = to_FIFOF_I (f_W); + interface o_B = to_FIFOF_O (f_B); - interface o_rd_addr = to_FIFOF_O (f_rd_addr); - interface i_rd_data = to_FIFOF_I (f_rd_data); + interface i_AR = to_FIFOF_I (f_AR); + interface o_R = to_FIFOF_O (f_R); endinterface endmodule -// ================================================================ -// M transactor interface - -interface AXI4_M_Xactor_IFC #(numeric type wd_id, - numeric type wd_addr, - numeric type wd_data, - numeric type wd_user); - method Action reset; - - // AXI side - interface AXI4_M_IFC #(wd_id, wd_addr, wd_data, wd_user) axi_side; - - // FIFOF side - interface FIFOF_I #(AXI4_Wr_Addr #(wd_id, wd_addr, wd_user)) i_wr_addr; - interface FIFOF_I #(AXI4_Wr_Data #(wd_data, wd_user)) i_wr_data; - interface FIFOF_O #(AXI4_Wr_Resp #(wd_id, wd_user)) o_wr_resp; - - interface FIFOF_I #(AXI4_Rd_Addr #(wd_id, wd_addr, wd_user)) i_rd_addr; - interface FIFOF_O #(AXI4_Rd_Data #(wd_id, wd_data, wd_user)) o_rd_data; -endinterface: AXI4_M_Xactor_IFC - -// ---------------------------------------------------------------- -// M transactor -// This version uses FIFOFs for total decoupling. - -module mkAXI4_M_Xactor (AXI4_M_Xactor_IFC #(wd_id, wd_addr, wd_data, wd_user)); - - Bool unguarded = True; - Bool guarded = False; - - // These FIFOs are guarded on BSV side, unguarded on AXI side - FIFOF #(AXI4_Wr_Addr #(wd_id, wd_addr, wd_user)) f_wr_addr <- mkGFIFOF (guarded, unguarded); - FIFOF #(AXI4_Wr_Data #(wd_data, wd_user)) f_wr_data <- mkGFIFOF (guarded, unguarded); - FIFOF #(AXI4_Wr_Resp #(wd_id, wd_user)) f_wr_resp <- mkGFIFOF (unguarded, guarded); - - FIFOF #(AXI4_Rd_Addr #(wd_id, wd_addr, wd_user)) f_rd_addr <- mkGFIFOF (guarded, unguarded); - FIFOF #(AXI4_Rd_Data #(wd_id, wd_data, wd_user)) f_rd_data <- mkGFIFOF (unguarded, guarded); - - // ---------------------------------------------------------------- - // INTERFACE - - method Action reset; - f_wr_addr.clear; - f_wr_data.clear; - f_wr_resp.clear; - f_rd_addr.clear; - f_rd_data.clear; - endmethod - - // AXI side - interface axi_side = interface AXI4_M_IFC; - // Wr Addr channel - method Bool m_awvalid = f_wr_addr.notEmpty; - method Bit #(wd_id) m_awid = f_wr_addr.first.awid; - method Bit #(wd_addr) m_awaddr = f_wr_addr.first.awaddr; - method Bit #(8) m_awlen = f_wr_addr.first.awlen; - method AXI4_Size m_awsize = f_wr_addr.first.awsize; - method Bit #(2) m_awburst = f_wr_addr.first.awburst; - method Bit #(1) m_awlock = f_wr_addr.first.awlock; - method Bit #(4) m_awcache = f_wr_addr.first.awcache; - method Bit #(3) m_awprot = f_wr_addr.first.awprot; - method Bit #(4) m_awqos = f_wr_addr.first.awqos; - method Bit #(4) m_awregion = f_wr_addr.first.awregion; - method Bit #(wd_user) m_awuser = f_wr_addr.first.awuser; - method Action m_awready (Bool awready); - if (f_wr_addr.notEmpty && awready) f_wr_addr.deq; - endmethod - - // Wr Data channel - method Bool m_wvalid = f_wr_data.notEmpty; - method Bit #(wd_data) m_wdata = f_wr_data.first.wdata; - method Bit #(TDiv #(wd_data, 8)) m_wstrb = f_wr_data.first.wstrb; - method Bool m_wlast = f_wr_data.first.wlast; - method Bit #(wd_user) m_wuser = f_wr_data.first.wuser; - method Action m_wready (Bool wready); - if (f_wr_data.notEmpty && wready) f_wr_data.deq; - endmethod - - // Wr Response channel - method Action m_bvalid (Bool bvalid, - Bit #(wd_id) bid, - Bit #(2) bresp, - Bit #(wd_user) buser); - if (bvalid && f_wr_resp.notFull) - f_wr_resp.enq (AXI4_Wr_Resp {bid: bid, - bresp: bresp, - buser: buser}); - endmethod - - method Bool m_bready; - return f_wr_resp.notFull; - endmethod - - // Rd Addr channel - method Bool m_arvalid = f_rd_addr.notEmpty; - method Bit #(wd_id) m_arid = f_rd_addr.first.arid; - method Bit #(wd_addr) m_araddr = f_rd_addr.first.araddr; - method Bit #(8) m_arlen = f_rd_addr.first.arlen; - method AXI4_Size m_arsize = f_rd_addr.first.arsize; - method Bit #(2) m_arburst = f_rd_addr.first.arburst; - method Bit #(1) m_arlock = f_rd_addr.first.arlock; - method Bit #(4) m_arcache = f_rd_addr.first.arcache; - method Bit #(3) m_arprot = f_rd_addr.first.arprot; - method Bit #(4) m_arqos = f_rd_addr.first.arqos; - method Bit #(4) m_arregion = f_rd_addr.first.arregion; - method Bit #(wd_user) m_aruser = f_rd_addr.first.aruser; - - method Action m_arready (Bool arready); - if (f_rd_addr.notEmpty && arready) f_rd_addr.deq; - endmethod - - // Rd Data channel - method Action m_rvalid (Bool rvalid, // in - Bit #(wd_id) rid, // in - Bit #(wd_data) rdata, // in - Bit #(2) rresp, // in - Bool rlast, // in - Bit #(wd_user) ruser); // in - if (rvalid && f_rd_data.notFull) - f_rd_data.enq (AXI4_Rd_Data {rid: rid, - rdata: rdata, - rresp: rresp, - rlast: rlast, - ruser: ruser}); - endmethod - - method Bool m_rready; - return f_rd_data.notFull; - endmethod - - endinterface; - - // FIFOF side - interface i_wr_addr = to_FIFOF_I (f_wr_addr); - interface i_wr_data = to_FIFOF_I (f_wr_data); - interface o_wr_resp = to_FIFOF_O (f_wr_resp); - - interface i_rd_addr = to_FIFOF_I (f_rd_addr); - interface o_rd_data = to_FIFOF_O (f_rd_data); -endmodule: mkAXI4_M_Xactor - -// ---------------------------------------------------------------- -// M transactor -// This version uses crgs and regs instead of FIFOFs. -// This uses 1/2 the resources, but introduces scheduling dependencies. - -module mkAXI4_M_Xactor_2 (AXI4_M_Xactor_IFC #(wd_id, wd_addr, wd_data, wd_user)); - - // Each crg_full, rg_data pair below represents a 1-element fifo. - - Array #(Reg #(Bool)) crg_wr_addr_full <- mkCReg (3, False); - Reg #(AXI4_Wr_Addr #(wd_id, wd_addr, wd_user)) rg_wr_addr <- mkRegU; - - Array #(Reg #(Bool)) crg_wr_data_full <- mkCReg (3, False); - Reg #(AXI4_Wr_Data #(wd_data, wd_user)) rg_wr_data <- mkRegU; - - Array #(Reg #(Bool)) crg_wr_resp_full <- mkCReg (3, False); - Reg #(AXI4_Wr_Resp #(wd_id, wd_user)) rg_wr_resp <- mkRegU; - - Array #(Reg #(Bool)) crg_rd_addr_full <- mkCReg (3, False); - Reg #(AXI4_Rd_Addr #(wd_id, wd_addr, wd_user)) rg_rd_addr <- mkRegU; - - Array #(Reg #(Bool)) crg_rd_data_full <- mkCReg (3, False); - Reg #(AXI4_Rd_Data #(wd_id, wd_data, wd_user)) rg_rd_data <- mkRegU; - - // The following CReg port indexes specify the relative scheduling of: - // {first,deq,notEmpty} {enq,notFull} clear - - // TODO: 'deq/enq/clear = 1/2/0' is unusual, but eliminates a - // scheduling cycle in Piccolo's DCache. Normally should be 0/1/2. - - Integer port_deq = 1; - Integer port_enq = 2; - Integer port_clear = 0; - - // ---------------------------------------------------------------- - // INTERFACE - - method Action reset; - crg_wr_addr_full [port_clear] <= False; - crg_wr_data_full [port_clear] <= False; - crg_wr_resp_full [port_clear] <= False; - crg_rd_addr_full [port_clear] <= False; - crg_rd_data_full [port_clear] <= False; - endmethod - - // AXI side - interface axi_side = interface AXI4_M_IFC; - // Wr Addr channel - method Bool m_awvalid = crg_wr_addr_full [port_deq]; - method Bit #(wd_id) m_awid = rg_wr_addr.awid; - method Bit #(wd_addr) m_awaddr = rg_wr_addr.awaddr; - method Bit #(8) m_awlen = rg_wr_addr.awlen; - method AXI4_Size m_awsize = rg_wr_addr.awsize; - method Bit #(2) m_awburst = rg_wr_addr.awburst; - method Bit #(1) m_awlock = rg_wr_addr.awlock; - method Bit #(4) m_awcache = rg_wr_addr.awcache; - method Bit #(3) m_awprot = rg_wr_addr.awprot; - method Bit #(4) m_awqos = rg_wr_addr.awqos; - method Bit #(4) m_awregion = rg_wr_addr.awregion; - method Bit #(wd_user) m_awuser = rg_wr_addr.awuser; - method Action m_awready (Bool awready); - if (crg_wr_addr_full [port_deq] && awready) - crg_wr_addr_full [port_deq] <= False; // deq - endmethod - - // Wr Data channel - method Bool m_wvalid = crg_wr_data_full [port_deq]; - method Bit #(wd_data) m_wdata = rg_wr_data.wdata; - method Bit #(TDiv #(wd_data, 8)) m_wstrb = rg_wr_data.wstrb; - method Bool m_wlast = rg_wr_data.wlast; - method Bit #(wd_user) m_wuser = rg_wr_data.wuser; - method Action m_wready (Bool wready); - if (crg_wr_data_full [port_deq] && wready) - crg_wr_data_full [port_deq] <= False; - endmethod - - // Wr Response channel - method Action m_bvalid (Bool bvalid, - Bit #(wd_id) bid, - Bit #(2) bresp, - Bit #(wd_user) buser); - if (bvalid && (! (crg_wr_resp_full [port_enq]))) begin - crg_wr_resp_full [port_enq] <= True; - rg_wr_resp <= AXI4_Wr_Resp {bid: bid, - bresp: bresp, - buser: buser}; - end - endmethod - - method Bool m_bready; - return (! (crg_wr_resp_full [port_enq])); - endmethod - - // Rd Addr channel - method Bool m_arvalid = crg_rd_addr_full [port_deq]; - method Bit #(wd_id) m_arid = rg_rd_addr.arid; - method Bit #(wd_addr) m_araddr = rg_rd_addr.araddr; - method Bit #(8) m_arlen = rg_rd_addr.arlen; - method AXI4_Size m_arsize = rg_rd_addr.arsize; - method Bit #(2) m_arburst = rg_rd_addr.arburst; - method Bit #(1) m_arlock = rg_rd_addr.arlock; - method Bit #(4) m_arcache = rg_rd_addr.arcache; - method Bit #(3) m_arprot = rg_rd_addr.arprot; - method Bit #(4) m_arqos = rg_rd_addr.arqos; - method Bit #(4) m_arregion = rg_rd_addr.arregion; - method Bit #(wd_user) m_aruser = rg_rd_addr.aruser; - method Action m_arready (Bool arready); - if (crg_rd_addr_full [port_deq] && arready) - crg_rd_addr_full [port_deq] <= False; // deq - endmethod - - // Rd Data channel - method Action m_rvalid (Bool rvalid, - Bit #(wd_id) rid, - Bit #(wd_data) rdata, - Bit #(2) rresp, - Bool rlast, - Bit #(wd_user) ruser); - if (rvalid && (! (crg_rd_data_full [port_enq]))) - crg_rd_data_full [port_enq] <= True; - rg_rd_data <= (AXI4_Rd_Data {rid: rid, - rdata: rdata, - rresp: rresp, - rlast: rlast, - ruser: ruser}); - endmethod - - method Bool m_rready; - return (! (crg_rd_data_full [port_enq])); - endmethod - - endinterface; - - // FIFOF side - interface i_wr_addr = fn_crg_and_rg_to_FIFOF_I (crg_wr_addr_full [port_enq], rg_wr_addr); - interface i_wr_data = fn_crg_and_rg_to_FIFOF_I (crg_wr_data_full [port_enq], rg_wr_data); - interface o_wr_resp = fn_crg_and_rg_to_FIFOF_O (crg_wr_resp_full [port_deq], rg_wr_resp); - - interface i_rd_addr = fn_crg_and_rg_to_FIFOF_I (crg_rd_addr_full [port_enq], rg_rd_addr); - interface o_rd_data = fn_crg_and_rg_to_FIFOF_O (crg_rd_data_full [port_deq], rg_rd_data); -endmodule: mkAXI4_M_Xactor_2 - -// ================================================================ -// S transactor interface - -interface AXI4_S_Xactor_IFC #(numeric type wd_id, - numeric type wd_addr, - numeric type wd_data, - numeric type wd_user); - method Action reset; - - // AXI side - interface AXI4_S_IFC #(wd_id, wd_addr, wd_data, wd_user) axi_side; - - // FIFOF side - interface FIFOF_O #(AXI4_Wr_Addr #(wd_id, wd_addr, wd_user)) o_wr_addr; - interface FIFOF_O #(AXI4_Wr_Data #(wd_data, wd_user)) o_wr_data; - interface FIFOF_I #(AXI4_Wr_Resp #(wd_id, wd_user)) i_wr_resp; - - interface FIFOF_O #(AXI4_Rd_Addr #(wd_id, wd_addr, wd_user)) o_rd_addr; - interface FIFOF_I #(AXI4_Rd_Data #(wd_id, wd_data, wd_user)) i_rd_data; -endinterface: AXI4_S_Xactor_IFC - -// ---------------------------------------------------------------- -// S transactor -// This version uses FIFOFs for total decoupling. - -module mkAXI4_S_Xactor (AXI4_S_Xactor_IFC #(wd_id, wd_addr, wd_data, wd_user)); - - Bool unguarded = True; - Bool guarded = False; - - // These FIFOs are guarded on BSV side, unguarded on AXI side - FIFOF #(AXI4_Wr_Addr #(wd_id, wd_addr, wd_user)) f_wr_addr <- mkGFIFOF (unguarded, guarded); - FIFOF #(AXI4_Wr_Data #(wd_data, wd_user)) f_wr_data <- mkGFIFOF (unguarded, guarded); - FIFOF #(AXI4_Wr_Resp #(wd_id, wd_user)) f_wr_resp <- mkGFIFOF (guarded, unguarded); - - FIFOF #(AXI4_Rd_Addr #(wd_id, wd_addr, wd_user)) f_rd_addr <- mkGFIFOF (unguarded, guarded); - FIFOF #(AXI4_Rd_Data #(wd_id, wd_data, wd_user)) f_rd_data <- mkGFIFOF (guarded, unguarded); - - // ---------------------------------------------------------------- - // INTERFACE - - method Action reset; - f_wr_addr.clear; - f_wr_data.clear; - f_wr_resp.clear; - f_rd_addr.clear; - f_rd_data.clear; - endmethod - - // AXI side - interface axi_side = interface AXI4_S_IFC; - // Wr Addr channel - method Action m_awvalid (Bool awvalid, - Bit #(wd_id) awid, - Bit #(wd_addr) awaddr, - Bit #(8) awlen, - AXI4_Size awsize, - Bit #(2) awburst, - Bit #(1) awlock, - Bit #(4) awcache, - Bit #(3) awprot, - Bit #(4) awqos, - Bit #(4) awregion, - Bit #(wd_user) awuser); - if (awvalid && f_wr_addr.notFull) - f_wr_addr.enq (AXI4_Wr_Addr {awid: awid, - awaddr: awaddr, - awlen: awlen, - awsize: awsize, - awburst: awburst, - awlock: awlock, - awcache: awcache, - awprot: awprot, - awqos: awqos, - awregion: awregion, - awuser: awuser}); - endmethod - - method Bool m_awready; - return f_wr_addr.notFull; - endmethod - - // Wr Data channel - method Action m_wvalid (Bool wvalid, - Bit #(wd_data) wdata, - Bit #(TDiv #(wd_data, 8)) wstrb, - Bool wlast, - Bit #(wd_user) wuser); - if (wvalid && f_wr_data.notFull) - f_wr_data.enq (AXI4_Wr_Data {wdata: wdata, - wstrb: wstrb, - wlast: wlast, - wuser: wuser}); - endmethod - - method Bool m_wready; - return f_wr_data.notFull; - endmethod - - // Wr Response channel - method Bool m_bvalid = f_wr_resp.notEmpty; - method Bit #(wd_id) m_bid = f_wr_resp.first.bid; - method Bit #(2) m_bresp = f_wr_resp.first.bresp; - method Bit #(wd_user) m_buser = f_wr_resp.first.buser; - method Action m_bready (Bool bready); - if (bready && f_wr_resp.notEmpty) - f_wr_resp.deq; - endmethod - - // Rd Addr channel - method Action m_arvalid (Bool arvalid, - Bit #(wd_id) arid, - Bit #(wd_addr) araddr, - Bit #(8) arlen, - AXI4_Size arsize, - Bit #(2) arburst, - Bit #(1) arlock, - Bit #(4) arcache, - Bit #(3) arprot, - Bit #(4) arqos, - Bit #(4) arregion, - Bit #(wd_user) aruser); - if (arvalid && f_rd_addr.notFull) - f_rd_addr.enq (AXI4_Rd_Addr {arid: arid, - araddr: araddr, - arlen: arlen, - arsize: arsize, - arburst: arburst, - arlock: arlock, - arcache: arcache, - arprot: arprot, - arqos: arqos, - arregion: arregion, - aruser: aruser}); - endmethod - - method Bool m_arready; - return f_rd_addr.notFull; - endmethod - - // Rd Data channel - method Bool m_rvalid = f_rd_data.notEmpty; - method Bit #(wd_id) m_rid = f_rd_data.first.rid; - method Bit #(wd_data) m_rdata = f_rd_data.first.rdata; - method Bit #(2) m_rresp = f_rd_data.first.rresp; - method Bool m_rlast = f_rd_data.first.rlast; - method Bit #(wd_user) m_ruser = f_rd_data.first.ruser; - method Action m_rready (Bool rready); - if (rready && f_rd_data.notEmpty) - f_rd_data.deq; - endmethod - endinterface; - - // FIFOF side - interface o_wr_addr = to_FIFOF_O (f_wr_addr); - interface o_wr_data = to_FIFOF_O (f_wr_data); - interface i_wr_resp = to_FIFOF_I (f_wr_resp); - - interface o_rd_addr = to_FIFOF_O (f_rd_addr); - interface i_rd_data = to_FIFOF_I (f_rd_data); -endmodule: mkAXI4_S_Xactor - -// ---------------------------------------------------------------- -// S transactor -// This version uses crgs and regs instead of FIFOFs. -// This uses 1/2 the resources, but introduces scheduling dependencies. - -module mkAXI4_S_Xactor_2 (AXI4_S_Xactor_IFC #(wd_id, wd_addr, wd_data, wd_user)); - - // Each crg_full, rg_data pair below represents a 1-element fifo. - - // These FIFOs are guarded on BSV side, unguarded on AXI side - Array #(Reg #(Bool)) crg_wr_addr_full <- mkCReg (3, False); - Reg #(AXI4_Wr_Addr #(wd_id, wd_addr, wd_user)) rg_wr_addr <- mkRegU; - - Array #(Reg #(Bool)) crg_wr_data_full <- mkCReg (3, False); - Reg #(AXI4_Wr_Data #(wd_data, wd_user)) rg_wr_data <- mkRegU; - - Array #(Reg #(Bool)) crg_wr_resp_full <- mkCReg (3, False); - Reg #(AXI4_Wr_Resp #(wd_id, wd_user)) rg_wr_resp <- mkRegU; - - Array #(Reg #(Bool)) crg_rd_addr_full <- mkCReg (3, False); - Reg #(AXI4_Rd_Addr #(wd_id, wd_addr, wd_user)) rg_rd_addr <- mkRegU; - - Array #(Reg #(Bool)) crg_rd_data_full <- mkCReg (3, False); - Reg #(AXI4_Rd_Data #(wd_id, wd_data, wd_user)) rg_rd_data <- mkRegU; - - // The following CReg port indexes specify the relative scheduling of: - // {first,deq,notEmpty} {enq,notFull} clear - Integer port_deq = 0; - Integer port_enq = 1; - Integer port_clear = 2; - - // ---------------------------------------------------------------- - // INTERFACE - - method Action reset; - crg_wr_addr_full [port_clear] <= False; - crg_wr_data_full [port_clear] <= False; - crg_wr_resp_full [port_clear] <= False; - crg_rd_addr_full [port_clear] <= False; - crg_rd_data_full [port_clear] <= False; - endmethod - - // AXI side - interface axi_side = interface AXI4_S_IFC; - // Wr Addr channel - method Action m_awvalid (Bool awvalid, - Bit #(wd_id) awid, - Bit #(wd_addr) awaddr, - Bit #(8) awlen, - AXI4_Size awsize, - Bit #(2) awburst, - Bit #(1) awlock, - Bit #(4) awcache, - Bit #(3) awprot, - Bit #(4) awqos, - Bit #(4) awregion, - Bit #(wd_user) awuser); - - if (awvalid && (! crg_wr_addr_full [port_enq])) begin - crg_wr_addr_full [port_enq] <= True; // enq - rg_wr_addr <= AXI4_Wr_Addr {awid: awid, - awaddr: awaddr, - awlen: awlen, - awsize: awsize, - awburst: awburst, - awlock: awlock, - awcache: awcache, - awprot: awprot, - awqos: awqos, - awregion: awregion, - awuser: awuser}; - end - endmethod - - method Bool m_awready; - return (! crg_wr_addr_full [port_enq]); - endmethod - - // Wr Data channel - method Action m_wvalid (Bool wvalid, - Bit #(wd_data) wdata, - Bit #(TDiv #(wd_data, 8)) wstrb, - Bool wlast, - Bit #(wd_user) wuser); - if (wvalid && (! crg_wr_data_full [port_enq])) begin - crg_wr_data_full [port_enq] <= True; // enq - rg_wr_data <= AXI4_Wr_Data {wdata: wdata, - wstrb: wstrb, - wlast: wlast, - wuser: wuser}; - end - endmethod - - method Bool m_wready; - return (! crg_wr_data_full [port_enq]); - endmethod - - // Wr Response channel - method Bool m_bvalid = crg_wr_resp_full [port_deq]; - method Bit #(wd_id) m_bid = rg_wr_resp.bid; - method Bit #(2) m_bresp = rg_wr_resp.bresp; - method Bit #(wd_user) m_buser = rg_wr_resp.buser; - method Action m_bready (Bool bready); - if (bready && crg_wr_resp_full [port_deq]) - crg_wr_resp_full [port_deq] <= False; // deq - endmethod - - // Rd Addr channel - method Action m_arvalid (Bool arvalid, - Bit #(wd_id) arid, - Bit #(wd_addr) araddr, - Bit #(8) arlen, - AXI4_Size arsize, - Bit #(2) arburst, - Bit #(1) arlock, - Bit #(4) arcache, - Bit #(3) arprot, - Bit #(4) arqos, - Bit #(4) arregion, - Bit #(wd_user) aruser); - if (arvalid && (! crg_rd_addr_full [port_enq])) begin - crg_rd_addr_full [port_enq] <= True; // enq - rg_rd_addr <= AXI4_Rd_Addr {arid: arid, - araddr: araddr, - arlen: arlen, - arsize: arsize, - arburst: arburst, - arlock: arlock, - arcache: arcache, - arprot: arprot, - arqos: arqos, - arregion: arregion, - aruser: aruser}; - end - endmethod - - method Bool m_arready; - return (! crg_rd_addr_full [port_enq]); - endmethod - - // Rd Data channel - method Bool m_rvalid = crg_rd_data_full [port_deq]; - method Bit #(wd_id) m_rid = rg_rd_data.rid; - method Bit #(wd_data) m_rdata = rg_rd_data.rdata; - method Bit #(2) m_rresp = rg_rd_data.rresp; - method Bool m_rlast = rg_rd_data.rlast; - method Bit #(wd_user) m_ruser = rg_rd_data.ruser; - method Action m_rready (Bool rready); - if (rready && crg_rd_data_full [port_deq]) - crg_rd_data_full [port_deq] <= False; // deq - endmethod - endinterface; - - // FIFOF side - interface o_wr_addr = fn_crg_and_rg_to_FIFOF_O (crg_wr_addr_full [port_deq], rg_wr_addr); - interface o_wr_data = fn_crg_and_rg_to_FIFOF_O (crg_wr_data_full [port_deq], rg_wr_data); - interface i_wr_resp = fn_crg_and_rg_to_FIFOF_I (crg_wr_resp_full [port_enq], rg_wr_resp); - - interface o_rd_addr = fn_crg_and_rg_to_FIFOF_O (crg_rd_addr_full [port_deq], rg_rd_addr); - interface i_rd_data = fn_crg_and_rg_to_FIFOF_I (crg_rd_data_full [port_enq], rg_rd_data); -endmodule: mkAXI4_S_Xactor_2 - -// ================================================================ +// **************************************************************** endpackage diff --git a/Libraries/AMBA_Fabrics/AXI4/AXI4_Widener.bsv b/Libraries/AMBA_Fabrics/AXI4/AXI4_Widener.bsv index bd51bbe..e189ed4 100644 --- a/Libraries/AMBA_Fabrics/AXI4/AXI4_Widener.bsv +++ b/Libraries/AMBA_Fabrics/AXI4/AXI4_Widener.bsv @@ -1,20 +1,28 @@ -// Copyright (c) 2020-2021 Bluespec, Inc. All Rights Reserved -// +// Copyright (c) 2020-2023 Bluespec, Inc. All Rights Reserved +// Copyright (c) 2024 Rishiyur S. Nikhil. + // SPDX-License-Identifier: BSD-3-Clause package AXI4_Widener; -// ================================================================ -// This package defines an AXI4-S-to-AXI4-M 'widener' module. +// **************************************************************** +// This package defines an AXI4-S-to-AXI4-M 'data widener' module. +// The module arguments are an M interface from upstream and an S +// interface to downstream. // The interfaces facing S and M differ in data-bus width // The S-side data bus is wider than the M-side by some multiple. -// The primary function is data-bus re-alignment due to widening. +// The primary function here is data-bus re-alignment due to widening. // NOTE: Does not support bursts yet (which would need reshaping the -// data beats, strobes, burst lengh, etc.) +// data beats, strobes, burst length, etc.) +// Use AXI4_Deburster in front, if needed. + +// **************************************************************** + +export mkAXI4_Widener; -// ================================================================ +// **************************************************************** // Bluespec library imports import Vector :: *; @@ -23,60 +31,43 @@ import SpecialFIFOs :: *; import ConfigReg :: *; // ---------------- -// BSV additional libs +// Bluespec misc. libs import Cur_Cycle :: *; import GetPut_Aux :: *; import Semi_FIFOF :: *; -// ================================================================ +// ---------------- // Project imports -import AXI4_Types :: *; +import AXI4_Types :: *; -// ================================================================ -// The interface for the widener module +// **************************************************************** +// Simulation verbosity during simulation on stdout for this package (edit as desired) +// 0: quiet +// 1: display rules -interface AXI4_Widener_IFC #(numeric type wd_id_t, - numeric type wd_addr_t, - numeric type m_wd_data_t, // narrower - numeric type s_wd_data_t, // wider - numeric type wd_user_t); - // From M - interface AXI4_S_IFC #(wd_id_t, wd_addr_t, m_wd_data_t, wd_user_t) from_M; - // To S - interface AXI4_M_IFC #(wd_id_t, wd_addr_t, s_wd_data_t, wd_user_t) to_S; -endinterface +Integer verbosity = 0; -// ================================================================ +// **************************************************************** // The Widener module -module mkAXI4_Widener (AXI4_Widener_IFC #(wd_id_t, wd_addr_t, m_wd_data_t, s_wd_data_t, wd_user_t)) +module mkAXI4_Widener #(AXI4_M_IFC #(wd_id_t, wd_addr_t, m_wd_data_t, wd_user_t) ifc_M, + AXI4_S_IFC #(wd_id_t, wd_addr_t, s_wd_data_t, wd_user_t) ifc_S) + (Empty) provisos (Mul #(8, m_wd_bytes_t, m_wd_data_t), Div #(m_wd_data_t, 8, m_wd_bytes_t), Mul #(8, s_wd_bytes_t, s_wd_data_t), Div #(s_wd_data_t, 8, s_wd_bytes_t), - Add #(m_wd_data_t, __a, s_wd_data_t), // m_wd_data <= s_wd_data ("widening") - Add #(m_wd_bytes_t, __b, s_wd_bytes_t), // m_wd_bytes <= s_wd_bytes ("widening") + Add #(m_wd_data_t, __a, s_wd_data_t), // m_wd_data <= s_wd_data ("widening") + Add #(m_wd_bytes_t, __b, s_wd_bytes_t), // m_wd_bytes <= s_wd_bytes ("widening") Log #(m_wd_bytes_t, log2_m_wd_bytes_t), Log #(s_wd_bytes_t, log2_s_wd_bytes_t), NumAlias #(word_index_t, TSub #(s_wd_bytes_t, m_wd_bytes_t))); - // 0 quiet; 1: display rules - Integer verbosity = 0; - Integer log2_m_wd_bytes = valueOf (log2_m_wd_bytes_t); Integer log2_s_wd_bytes = valueOf (log2_s_wd_bytes_t); - // ---------------- - // Transactor facing M - AXI4_S_Xactor_IFC #(wd_id_t, wd_addr_t, m_wd_data_t, wd_user_t) - xactor_from_M <- mkAXI4_S_Xactor; - - // Transactor facing S - AXI4_M_Xactor_IFC #(wd_id_t, wd_addr_t, s_wd_data_t, wd_user_t) - xactor_to_S <- mkAXI4_M_Xactor; - // size covers latency to mem read response FIFOF #(Bit #(wd_addr_t)) f_araddrs <- mkSizedFIFOF (8); @@ -111,96 +102,95 @@ module mkAXI4_Widener (AXI4_Widener_IFC #(wd_id_t, wd_addr_t, m_wd_data_t, s_wd_ endfunction // ---------------- - // Wr requests (AW and W channels) + // AW and W channels (write requests) - rule rl_wr_xaction_M_to_S; - AXI4_Wr_Addr #(wd_id_t, wd_addr_t, wd_user_t) m_wra <- pop_o (xactor_from_M.o_wr_addr); - AXI4_Wr_Data #(m_wd_data_t, wd_user_t) m_wrd <- pop_o (xactor_from_M.o_wr_data); + rule rl_AW_W; + AXI4_AW #(wd_id_t, wd_addr_t, wd_user_t) m_aw <- pop_o (ifc_M.o_AW); + AXI4_W #(m_wd_data_t, wd_user_t) m_w <- pop_o (ifc_M.o_W); - let s_wra = m_wra; + let s_aw = m_aw; - match { .s_wdata, .s_wstrb} = fv_align_to_wider (m_wra.awaddr, m_wrd.wdata, m_wrd.wstrb); - AXI4_Wr_Data #(s_wd_data_t, wd_user_t) s_wrd = AXI4_Wr_Data {wdata: s_wdata, - wstrb: s_wstrb, - wlast: m_wrd.wlast, - wuser: m_wrd.wuser}; + match { .s_wdata, .s_wstrb} = fv_align_to_wider (m_aw.awaddr, m_w.wdata, m_w.wstrb); + AXI4_W #(s_wd_data_t, wd_user_t) s_w = AXI4_W {wdata: s_wdata, + wstrb: s_wstrb, + wlast: m_w.wlast, + wuser: m_w.wuser}; // Send to S - xactor_to_S.i_wr_addr.enq (s_wra); - xactor_to_S.i_wr_data.enq (s_wrd); + ifc_S.i_AW.enq (s_aw); + ifc_S.i_W.enq (s_w); // Debugging if (verbosity > 0) begin - $display ("%0d: %m:AXI4_Widener.rl_wr_xaction_M_to_S: m -> s", cur_cycle); - $display (" m_wra : ", fshow (m_wra)); - $display (" m_wrd: ", fshow (m_wrd)); - $display (" s_wrd: ", fshow (s_wrd)); + $display ("%0d: AXI4_Widener.rl_AW_W: m -> s", cur_cycle); + $display (" m_aw : ", fshow (m_aw)); + $display (" m_w: ", fshow (m_w)); + $display (" s_w: ", fshow (s_w)); end - endrule: rl_wr_xaction_M_to_S + endrule // ---------------- - // Wr responses (B channel): just pass through as-is. + // B channel (write responses): just pass through as-is. - rule rl_wr_resp_S_to_M; - AXI4_Wr_Resp #(wd_id_t, wd_user_t) s_wrr <- pop_o (xactor_to_S.o_wr_resp); - let m_wrr = s_wrr; - xactor_from_M.i_wr_resp.enq (m_wrr); + rule rl_B; + AXI4_B #(wd_id_t, wd_user_t) s_b <- pop_o (ifc_S.o_B); + let m_b = s_b; + ifc_M.i_B.enq (m_b); if (verbosity > 1) begin - $display ("%0d: %m::AXI4_Widener.rl_wr_resp_S_to_M: m <- s", cur_cycle); - $display (" s_wrr: ", fshow (s_wrr)); - $display (" m_wrr: ", fshow (m_wrr)); + $display ("%0d: AXI4_Widener.rl_B: m <- s", cur_cycle); + $display (" s_b: ", fshow (s_b)); + $display (" m_b: ", fshow (m_b)); end endrule // ---------------- - // Rd requests (AR channel); just pass it through, as-is + // AR channel (read requests); just pass it through, as-is // but remember the addr in order to align the data response. - rule rl_rd_xaction_M_to_S; - AXI4_Rd_Addr #(wd_id_t, wd_addr_t, wd_user_t) m_rda <- pop_o (xactor_from_M.o_rd_addr); - let s_rda = m_rda; - xactor_to_S.i_rd_addr.enq (s_rda); + rule rl_AR; + AXI4_AR #(wd_id_t, wd_addr_t, wd_user_t) m_ar <- pop_o (ifc_M.o_AR); + let s_ar = m_ar; + ifc_S.i_AR.enq (s_ar); - f_araddrs.enq (m_rda.araddr); + f_araddrs.enq (m_ar.araddr); // Debugging if (verbosity > 0) begin - $display ("%0d: %m::AXI4_Widener.rl_rd_xaction_M_to_S: m -> s", cur_cycle); - $display (" m_rda: ", fshow (m_rda)); - $display (" s_rda: ", fshow (s_rda)); + $display ("%0d: AXI4_Widener.rl_AR: m -> s", cur_cycle); + $display (" m_ar: ", fshow (m_ar)); + $display (" s_ar: ", fshow (s_ar)); end - endrule: rl_rd_xaction_M_to_S + endrule // ---------------- - // Rd responses + // R channel (read responses) - rule rl_rd_resp_S_to_M; - AXI4_Rd_Data #(wd_id_t, s_wd_data_t, wd_user_t) s_rdd <- pop_o (xactor_to_S.o_rd_data); + rule rl_R; + AXI4_R #(wd_id_t, s_wd_data_t, wd_user_t) s_r <- pop_o (ifc_S.o_R); let araddr <- pop (f_araddrs); - let m_rdata = fv_align_to_narrower (araddr, s_rdd.rdata); - AXI4_Rd_Data #(wd_id_t, m_wd_data_t, wd_user_t) m_rdd = AXI4_Rd_Data {rid: s_rdd.rid, - rdata: m_rdata, - rresp: s_rdd.rresp, - rlast: s_rdd.rlast, - ruser: s_rdd.ruser}; - xactor_from_M.i_rd_data.enq (m_rdd); + let m_rdata = fv_align_to_narrower (araddr, s_r.rdata); + AXI4_R #(wd_id_t, m_wd_data_t, wd_user_t) m_r = AXI4_R {rid: s_r.rid, + rdata: m_rdata, + rresp: s_r.rresp, + rlast: s_r.rlast, + ruser: s_r.ruser}; + ifc_M.i_R.enq (m_r); // Debugging if (verbosity > 0) begin - $display ("%0d: %m::AXI4_Widener.rl_rd_resp_S_to_M: m <- s", cur_cycle); - $display (" s_rdd: ", fshow (s_rdd)); - $display (" m_rdd: ", fshow (m_rdd)); + $display ("%0d: AXI4_Widener.rl_R: m <- s", cur_cycle); + $display (" s_r: ", fshow (s_r)); + $display (" m_r: ", fshow (m_r)); end - endrule: rl_rd_resp_S_to_M + endrule - // ---------------------------------------------------------------- + // ================================================================ // INTERFACE - interface from_M = xactor_from_M.axi_side; - interface to_S = xactor_to_S.axi_side; + // Empty endmodule -// ================================================================ +// **************************************************************** endpackage: AXI4_Widener diff --git a/Libraries/AMBA_Fabrics/AXI4/AXI4_Xactors.bsv b/Libraries/AMBA_Fabrics/AXI4/AXI4_Xactors.bsv deleted file mode 100644 index 6389b75..0000000 --- a/Libraries/AMBA_Fabrics/AXI4/AXI4_Xactors.bsv +++ /dev/null @@ -1,336 +0,0 @@ -// Copyright (c) 2022 University of Cambridge Computer Laboratory. -// Copyright (c) 2022 Bluespec, Inc. -// Authors: U.Cambridge CL -> Joe Stoy -> Rishiyur Nikhil -// -// SPDX-License-Identifier: BSD-3-Clause - -package AXI4_Xactors; - -// ================================================================ -// These are M and S transactors for AXI4, i.e., converters between -// BSV FIFO-like interfaces (flow-controlled) -// ARM AXI4 signals (ready/valid protocol) - -// ================================================================ -// BSV library imports - -// None - -// ---------------- -// BSV additional libs - -import Semi_FIFOF :: *; - -// ---------------- -// Local imports - -import AXI4_Types :: *; - -// ================================================================ -// Typeclass to convert -// normal FIFO_I and FIFO_O interfaces -// to unguarded FIFO_I and FIFO_O interfaces. - -typeclass ToUnguarded #(type a); - module mkUnguarded #(a x)(a); -endtypeclass - -instance ToUnguarded #(FIFOF_I #(a)) - provisos (Bits#(a, __)); - module mkUnguarded #(FIFOF_I #(a) ifc)(FIFOF_I #(a)); - let enqWire <- mkRWire; - rule warnDoEnq (isValid(enqWire.wget) && !ifc.notFull); - $display("WARNING: enqing into an already full FIFOF_I"); - $finish(0); - endrule - rule doEnq (isValid(enqWire.wget)); - ifc.enq(enqWire.wget.Valid); - endrule - return interface FIFOF_I; - method notFull = ifc.notFull; - method enq = enqWire.wset; - endinterface; - endmodule -endinstance - -instance ToUnguarded #(FIFOF_O #(a)) - provisos (Bits#(a, _)); - module mkUnguarded#(FIFOF_O #(a) ifc)(FIFOF_O#(a)); - let firstWire <- mkDWire(unpack(0)); - let deqWire <- mkPulseWire; - rule setFirst; firstWire <= ifc.first; endrule - rule warnDoDeq (deqWire && !ifc.notEmpty); - $display("WARNING: deqing from empty FIFOF_O"); - $finish(0); - endrule - rule doDeq (deqWire && ifc.notEmpty); - ifc.deq; - endrule - return interface FIFOF_O; - method notEmpty = ifc.notEmpty; - method first = firstWire; - method deq = deqWire.send; - endinterface; - endmodule -endinstance - -// ================================================================ -// M transactor -// Arguments aw, w, b, ar and r are the standard five AXI4 channels, as normal FIFOFs. -// Returns ARM-signalling interface. - -module mkAXI4_Xactor_M_3 - #(aw_t aw, w_t w, b_t b, ar_t ar, r_t r) - (AXI4_M_IFC #(wd_id, wd_addr, wd_data, wd_user)) - - provisos (To_FIFOF_IO #(aw_t, AXI4_Wr_Addr #(wd_id, wd_addr, wd_user)), - To_FIFOF_IO #(w_t, AXI4_Wr_Data #(wd_data, wd_user)), - To_FIFOF_IO #(b_t, AXI4_Wr_Resp #(wd_id, wd_user)), - To_FIFOF_IO #(ar_t, AXI4_Rd_Addr #(wd_id, wd_addr, wd_user)), - To_FIFOF_IO #(r_t, AXI4_Rd_Data #(wd_id, wd_data, wd_user))); - - // For each argument FIFOF, unguard the ARM-signalling side. - - FIFOF_O #(AXI4_Wr_Addr #(wd_id, wd_addr, wd_user)) - f_wr_addr <- mkUnguarded (to_FIFOF_O (aw)); - - FIFOF_O #(AXI4_Wr_Data #(wd_data, wd_user)) - f_wr_data <- mkUnguarded (to_FIFOF_O (w)); - - FIFOF_I #(AXI4_Wr_Resp #(wd_id, wd_user)) - f_wr_resp <- mkUnguarded (to_FIFOF_I (b)); - - FIFOF_O #(AXI4_Rd_Addr #(wd_id, wd_addr, wd_user)) - f_rd_addr <- mkUnguarded (to_FIFOF_O (ar)); - - FIFOF_I #(AXI4_Rd_Data #(wd_id, wd_data, wd_user)) - f_rd_data <- mkUnguarded (to_FIFOF_I (r)); - - // ---------------------------------------------------------------- - // INTERFACE (ARM signals) - - return interface AXI4_M_IFC; - // Wr Addr channel - method Bool m_awvalid = f_wr_addr.notEmpty; - method Bit #(wd_id) m_awid = f_wr_addr.first.awid; - method Bit #(wd_addr) m_awaddr = f_wr_addr.first.awaddr; - method Bit #(8) m_awlen = f_wr_addr.first.awlen; - method AXI4_Size m_awsize = f_wr_addr.first.awsize; - method Bit #(2) m_awburst = f_wr_addr.first.awburst; - method Bit #(1) m_awlock = f_wr_addr.first.awlock; - method Bit #(4) m_awcache = f_wr_addr.first.awcache; - method Bit #(3) m_awprot = f_wr_addr.first.awprot; - method Bit #(4) m_awqos = f_wr_addr.first.awqos; - method Bit #(4) m_awregion = f_wr_addr.first.awregion; - method Bit #(wd_user) m_awuser = f_wr_addr.first.awuser; - method Action m_awready (Bool awready); - if (f_wr_addr.notEmpty && awready) f_wr_addr.deq; - endmethod - - // Wr Data channel - method Bool m_wvalid = f_wr_data.notEmpty; - method Bit #(wd_data) m_wdata = f_wr_data.first.wdata; - method Bit #(TDiv #(wd_data, 8)) m_wstrb = f_wr_data.first.wstrb; - method Bool m_wlast = f_wr_data.first.wlast; - method Bit #(wd_user) m_wuser = f_wr_data.first.wuser; - method Action m_wready (Bool wready); - if (f_wr_data.notEmpty && wready) f_wr_data.deq; - endmethod - - // Wr Response channel - method Action m_bvalid (Bool bvalid, - Bit #(wd_id) bid, - Bit #(2) bresp, - Bit #(wd_user) buser); - if (bvalid && f_wr_resp.notFull) - f_wr_resp.enq (AXI4_Wr_Resp {bid: bid, - bresp: bresp, - buser: buser}); - endmethod - - method Bool m_bready; - return f_wr_resp.notFull; - endmethod - - // Rd Addr channel - method Bool m_arvalid = f_rd_addr.notEmpty; - method Bit #(wd_id) m_arid = f_rd_addr.first.arid; - method Bit #(wd_addr) m_araddr = f_rd_addr.first.araddr; - method Bit #(8) m_arlen = f_rd_addr.first.arlen; - method AXI4_Size m_arsize = f_rd_addr.first.arsize; - method Bit #(2) m_arburst = f_rd_addr.first.arburst; - method Bit #(1) m_arlock = f_rd_addr.first.arlock; - method Bit #(4) m_arcache = f_rd_addr.first.arcache; - method Bit #(3) m_arprot = f_rd_addr.first.arprot; - method Bit #(4) m_arqos = f_rd_addr.first.arqos; - method Bit #(4) m_arregion = f_rd_addr.first.arregion; - method Bit #(wd_user) m_aruser = f_rd_addr.first.aruser; - - method Action m_arready (Bool arready); - if (f_rd_addr.notEmpty && arready) f_rd_addr.deq; - endmethod - - // Rd Data channel - method Action m_rvalid (Bool rvalid, // in - Bit #(wd_id) rid, // in - Bit #(wd_data) rdata, // in - Bit #(2) rresp, // in - Bool rlast, // in - Bit #(wd_user) ruser); // in - if (rvalid && f_rd_data.notFull) - f_rd_data.enq (AXI4_Rd_Data {rid: rid, - rdata: rdata, - rresp: rresp, - rlast: rlast, - ruser: ruser}); - endmethod - - method Bool m_rready; - return f_rd_data.notFull; - endmethod - - endinterface; -endmodule: mkAXI4_Xactor_M_3 - -// ================================================================ -// S transactor -// Arguments aw, w, b, ar and r are the standard five AXI4 channels, as normal FIFOFs. -// Returns ARM-signalling interface. - -module mkAXI4_Xactor_S_3 - #(aw_t aw, w_t w, b_t b, ar_t ar, r_t r) - (AXI4_S_IFC #(wd_id, wd_addr, wd_data, wd_user)) - - provisos (To_FIFOF_IO #(aw_t, AXI4_Wr_Addr #(wd_id, wd_addr, wd_user)), - To_FIFOF_IO #(w_t, AXI4_Wr_Data #(wd_data, wd_user)), - To_FIFOF_IO #(b_t, AXI4_Wr_Resp #(wd_id, wd_user)), - To_FIFOF_IO #(ar_t, AXI4_Rd_Addr #(wd_id, wd_addr, wd_user)), - To_FIFOF_IO #(r_t, AXI4_Rd_Data #(wd_id, wd_data, wd_user))); - - // For each argument FIFOF, unguard the ARM-signalling side. - - FIFOF_I #(AXI4_Wr_Addr #(wd_id, wd_addr, wd_user)) - f_wr_addr <- mkUnguarded (to_FIFOF_I (aw)); - - FIFOF_I #(AXI4_Wr_Data #(wd_data, wd_user)) - f_wr_data <- mkUnguarded (to_FIFOF_I (w)); - - FIFOF_O #(AXI4_Wr_Resp #(wd_id, wd_user)) - f_wr_resp <- mkUnguarded (to_FIFOF_O (b)); - - FIFOF_I #(AXI4_Rd_Addr #(wd_id, wd_addr, wd_user)) - f_rd_addr <- mkUnguarded (to_FIFOF_I (ar)); - - FIFOF_O #(AXI4_Rd_Data #(wd_id, wd_data, wd_user)) - f_rd_data <- mkUnguarded (to_FIFOF_O (r)); - - // ---------------------------------------------------------------- - // INTERFACE (ARM signals) - - return interface AXI4_S_IFC; - // Wr Addr channel - method Action m_awvalid (Bool awvalid, - Bit #(wd_id) awid, - Bit #(wd_addr) awaddr, - Bit #(8) awlen, - AXI4_Size awsize, - Bit #(2) awburst, - Bit #(1) awlock, - Bit #(4) awcache, - Bit #(3) awprot, - Bit #(4) awqos, - Bit #(4) awregion, - Bit #(wd_user) awuser); - if (awvalid && f_wr_addr.notFull) - f_wr_addr.enq (AXI4_Wr_Addr {awid: awid, - awaddr: awaddr, - awlen: awlen, - awsize: awsize, - awburst: awburst, - awlock: awlock, - awcache: awcache, - awprot: awprot, - awqos: awqos, - awregion: awregion, - awuser: awuser}); - endmethod - - method Bool m_awready; - return f_wr_addr.notFull; - endmethod - - // Wr Data channel - method Action m_wvalid (Bool wvalid, - Bit #(wd_data) wdata, - Bit #(TDiv #(wd_data, 8)) wstrb, - Bool wlast, - Bit #(wd_user) wuser); - if (wvalid && f_wr_data.notFull) - f_wr_data.enq (AXI4_Wr_Data {wdata: wdata, - wstrb: wstrb, - wlast: wlast, - wuser: wuser}); - endmethod - - method Bool m_wready; - return f_wr_data.notFull; - endmethod - - // Wr Response channel - method Bool m_bvalid = f_wr_resp.notEmpty; - method Bit #(wd_id) m_bid = f_wr_resp.first.bid; - method Bit #(2) m_bresp = f_wr_resp.first.bresp; - method Bit #(wd_user) m_buser = f_wr_resp.first.buser; - method Action m_bready (Bool bready); - if (bready && f_wr_resp.notEmpty) - f_wr_resp.deq; - endmethod - - // Rd Addr channel - method Action m_arvalid (Bool arvalid, - Bit #(wd_id) arid, - Bit #(wd_addr) araddr, - Bit #(8) arlen, - AXI4_Size arsize, - Bit #(2) arburst, - Bit #(1) arlock, - Bit #(4) arcache, - Bit #(3) arprot, - Bit #(4) arqos, - Bit #(4) arregion, - Bit #(wd_user) aruser); - if (arvalid && f_rd_addr.notFull) - f_rd_addr.enq (AXI4_Rd_Addr {arid: arid, - araddr: araddr, - arlen: arlen, - arsize: arsize, - arburst: arburst, - arlock: arlock, - arcache: arcache, - arprot: arprot, - arqos: arqos, - arregion: arregion, - aruser: aruser}); - endmethod - - method Bool m_arready; - return f_rd_addr.notFull; - endmethod - - // Rd Data channel - method Bool m_rvalid = f_rd_data.notEmpty; - method Bit #(wd_id) m_rid = f_rd_data.first.rid; - method Bit #(wd_data) m_rdata = f_rd_data.first.rdata; - method Bit #(2) m_rresp = f_rd_data.first.rresp; - method Bool m_rlast = f_rd_data.first.rlast; - method Bit #(wd_user) m_ruser = f_rd_data.first.ruser; - method Action m_rready (Bool rready); - if (rready && f_rd_data.notEmpty) - f_rd_data.deq; - endmethod - endinterface; -endmodule: mkAXI4_Xactor_S_3 - -// ================================================================ - -endpackage diff --git a/Libraries/AMBA_Fabrics/AXI4/AXI4_to_LD.bsv b/Libraries/AMBA_Fabrics/AXI4/AXI4_to_LD.bsv index 365dfa6..c47817d 100644 --- a/Libraries/AMBA_Fabrics/AXI4/AXI4_to_LD.bsv +++ b/Libraries/AMBA_Fabrics/AXI4/AXI4_to_LD.bsv @@ -1,6 +1,6 @@ -// Copyright (c) 2021 Rishiyur S. Nikhil and Bluespec, Inc. All Rights Reserved +// Copyright (c) 2021-2024 Rishiyur S. Nikhil and Bluespec, Inc. All Rights Reserved // Author: Rishiyur S. Nikhil -// + // SPDX-License-Identifier: BSD-3-Clause package AXI4_to_LD; @@ -22,7 +22,7 @@ import Vector :: *; import FIFOF :: *; // ---------------- -// BSV additional libs +// Bluespec misc. libs import Cur_Cycle :: *; import Semi_FIFOF :: *; @@ -71,8 +71,8 @@ Integer verbosity = 0; // Loads: Module module mkAXI4_to_LD - #(FIFOF_O #(AXI4_Rd_Addr #(wd_id_t, wd_addr_t, wd_user_t)) o_rd_addr, - FIFOF_I #(AXI4_Rd_Data #(wd_id_t, wd_axi_data_t, wd_user_t)) i_rd_data) + #(FIFOF_O #(AXI4_AR #(wd_id_t, wd_addr_t, wd_user_t)) o_rd_addr, + FIFOF_I #(AXI4_R #(wd_id_t, wd_axi_data_t, wd_user_t)) i_rd_data) (AXI4_to_LD_IFC #(wd_addr_t, wd_ldst_data_t)) provisos (Add #(a__, 8, wd_addr_t), @@ -157,10 +157,11 @@ module mkAXI4_to_LD Bit #(wd_addr_t) addr_axi_bus_lo = fn_addr_to_NAPOT (araddr, fromInteger (wdB_axi_data_I)); // Bytelane of araddr on AXI data - Bit #(8) addr_bytelane = fn_addr_to_axi_data_bytelane (araddr, wdB_axi_data_I); + Bit #(8) addr_bytelane = fn_addr_to_axi_data_bytelane (araddr, wdB_axi_data_I); // ARSIZE specifies a NAPOT window around araddr, ... - Bit #(8) wdB_szwindow_B = fv_AXI4_Size_to_num_bytes (rd_addr_S.arsize); + Bit #(8) wdB_szwindow_B = fv_AXI4_Size_to_num_bytes (rd_addr_S.arsize); + // Address of NAPOT ARSIZE window containing araddr Bit #(wd_addr_t) addr_szwindow_lo = fn_addr_to_NAPOT (araddr, wdB_szwindow_B); @@ -300,7 +301,7 @@ module mkAXI4_to_LD rule rl_start_xaction (rg_state == STATE_IDLE); if (verbosity > 0) begin - $display ("%0d: %m.AXI4_to_LD:rl_start_xaction ================", cur_cycle); + $display ("%0d: AXI4_to_LD:rl_start_xaction ================", cur_cycle); fa_show_axi_req_values(); end @@ -309,7 +310,7 @@ module mkAXI4_to_LD Bool illegal_req = False; if (wdB_szwindow_B > fromInteger (wdB_axi_data_I)) begin if (verbosity == 0) - $display ("%0d: %m.AXI4_to_LD:rl_start_xaction ================", cur_cycle); + $display ("%0d: AXI4_to_LD:rl_start_xaction ================", cur_cycle); $display (" ERROR: illegal AXI4 request"); $display (" awsize 0x%0h bytes > axi data bus width 0x%0h bytes", wdB_szwindow_B, wdB_axi_data_I); @@ -318,7 +319,7 @@ module mkAXI4_to_LD if (rd_addr_S.arlen != 0) begin if (verbosity == 0) - $display ("%0d: %m.AXI4_to_LD:rl_start_xaction ================", cur_cycle); + $display ("%0d: AXI4_to_LD:rl_start_xaction ================", cur_cycle); $display (" ERROR: illegal AXI4 request"); $display (" arlen 0x%0h; only arlen 0 (1-beat bursts) supported", rd_addr_S.arlen); @@ -353,7 +354,7 @@ module mkAXI4_to_LD rule rl_next_slice (rg_state == STATE_SLICE); if (verbosity > 0) - $display ("%0d: %m.AXI4_to_LD:rl_next_slice", cur_cycle); + $display ("%0d: AXI4_to_LD:rl_next_slice", cur_cycle); Bit #(8) bytelane_slice_lo = rg_bytelane_slice_lo; Bit #(8) bytelane_slice_hi = rg_bytelane_slice_lo + fromInteger (wdB_ldst_data_I - 1); @@ -371,7 +372,7 @@ module mkAXI4_to_LD rule rl_partial (rg_state == STATE_PARTIAL); if (verbosity > 0) begin - $display ("%0d: %m.AXI4_to_LD:rl_partial", cur_cycle); + $display ("%0d: AXI4_to_LD:rl_partial", cur_cycle); $display (" rg_bytelane_hi..lo [%0h..%0h] rg_bytelane_slice_lo %0h", rg_bytelane_hi, rg_bytelane_lo, rg_bytelane_slice_lo); end @@ -400,7 +401,7 @@ module mkAXI4_to_LD rule rl_finish_req (rg_state == STATE_FINISH_REQ); if (verbosity > 0) - $display ("%0d: %m.AXI4_to_LD:rl_finish_ld_req", cur_cycle); + $display ("%0d: AXI4_to_LD:rl_finish_ld_req", cur_cycle); f_ld_rsp_info.enq (tuple2 (RSP_DONE, ?)); // 'done' sentinel f_axi_rsp_info.enq (tuple3 (False, rd_addr_S.arid, rd_addr_S.aruser)); @@ -433,7 +434,7 @@ module mkAXI4_to_LD rg_v_slice <= v_slice; if (verbosity > 0) begin - $display ("%0d: %m.AXI4_to_LD:rl_handle_ld_rsp: err = %0h data %0h", cur_cycle, + $display ("%0d: AXI4_to_LD:rl_handle_ld_rsp: err = %0h data %0h", cur_cycle, err, ld_data); $display (" ShiftInAtN slice %016h; new v_slice", slice); fa_show_v_slices (v_slice); @@ -450,7 +451,7 @@ module mkAXI4_to_LD rg_remaining_slices <= rg_remaining_slices - 1; if (verbosity > 0) begin - $display ("%0d: %m.AXI4_to_LD:rl_handle_ld_slice_ignore: skipping slice", cur_cycle); + $display ("%0d: AXI4_to_LD:rl_handle_ld_slice_ignore: skipping slice", cur_cycle); $display (" new v_slice"); fa_show_v_slices (v_slice); end @@ -464,7 +465,7 @@ module mkAXI4_to_LD rg_remaining_slices <= rg_remaining_slices - 1; if (verbosity > 0) begin - $display ("%0d: %m.AXI4_to_LD:rl_send_axi_response; shift only; new v_slice", cur_cycle); + $display ("%0d: AXI4_to_LD:rl_send_axi_response; shift only; new v_slice", cur_cycle); fa_show_v_slices (v_slice); end endrule @@ -477,21 +478,21 @@ module mkAXI4_to_LD Bit #(wd_axi_data_t) rdata = pack (rg_v_slice); - let rd_data_S = AXI4_Rd_Data {rid: rid, - rresp: ((illegal_req || rg_cumulative_err) - ? axi4_resp_slverr - : axi4_resp_okay), - rdata: rdata, - ruser: ruser, - rlast: True}; + let rd_data_S = AXI4_R {rid: rid, + rresp: ((illegal_req || rg_cumulative_err) + ? axi4_resp_slverr + : axi4_resp_okay), + rdata: rdata, + ruser: ruser, + rlast: True}; i_rd_data.enq (rd_data_S); // Get ready for next axi transaction rg_remaining_slices <= fromInteger (slices_per_axi_data_I); if (verbosity > 0) begin - $display ("%0d: %m.AXI4_to_LD:rl_send_axi_response", cur_cycle); - $display (" AXI4_Rd_Data rid %0h rresp %0h rlast %0d ruser %0h", + $display ("%0d: AXI4_to_LD:rl_send_axi_response", cur_cycle); + $display (" AXI4_R rid %0h rresp %0h rlast %0d ruser %0h", rd_data_S.rid, rd_data_S.rresp, rd_data_S.rlast, rd_data_S.ruser); fa_show_v_slices (rg_v_slice); end @@ -506,7 +507,7 @@ module mkAXI4_to_LD rule rl_illegal_req (rg_state == STATE_ILLEGAL_REQ); if (verbosity > 0) - $display ("%0d: %m.AXI4_to_LD:rl_illegal_req", cur_cycle); + $display ("%0d: AXI4_to_LD:rl_illegal_req", cur_cycle); f_ld_rsp_info.enq (tuple2 (RSP_DONE, ?)); f_axi_rsp_info.enq (tuple3 (True, rd_addr_S.arid, rd_addr_S.aruser)); diff --git a/Libraries/AMBA_Fabrics/AXI4/AXI4_to_LDST.bsv b/Libraries/AMBA_Fabrics/AXI4/AXI4_to_LDST.bsv index 4ed8514..83de7cb 100644 --- a/Libraries/AMBA_Fabrics/AXI4/AXI4_to_LDST.bsv +++ b/Libraries/AMBA_Fabrics/AXI4/AXI4_to_LDST.bsv @@ -1,20 +1,20 @@ -// Copyright (c) 2021 Rishiyur S. Nikhil and Bluespec, Inc. All Rights Reserved -// Author: Rishiyur S. Nikhil -// +// Copyright (c) 2021-2024 Rishiyur S. Nikhil and Bluespec, Inc. All Rights Reserved + // SPDX-License-Identifier: BSD-3-Clause package AXI4_to_LDST; // ================================================================ // This package defines module mkAXI4_to_LDST and its interface. -// The axi4_S sub-interface is an AXI4 subordinate for single (not burst) transactions. -// The ldst_M sub-interface is a simple memory Load/Store interface where -// - the op is LB, LH, LW, LD, SB, SH, SW, SD -// for loads/stores of bytes (8b), halfwords (16b), words (32b), doublewords (64b), -// but only up to the ldst_M data bus width +// The module's argment is an AXI4_M_IFC from some upstream M. +// It does not accept bursts (use a Deburster in front, if needed). + +// The module's interface has 4 FIFOs for LO/ST requests and responses. +// Load and store requests +// - have a width (B/H/W/D) for 1/2/4/8 bytes (but limited to wd_axi_data) // - addresses are properly aligned (lsbs are 0 for H, 00 for W and 000 for D) // - data are always in LSBS no matter the address (unlike AXI4's lane-alignment) -// - The axi4_S data bus width must be >= ldst_M bus width +// - wd_axi_dat must be >= wd_ldst_data // - This module 'slices' the axi4_S data (width wd_axi_data) // into slices of width wd_ldst_data for the load/store data bus. @@ -23,7 +23,7 @@ package AXI4_to_LDST; // NAPOT naturally aligned power of two // wd_... width in bits // wdB_... width in bytes -// wd_..._t width as a type (numeric kind) +// wd_... width as a type (numeric kind) // wd_..._I width as an Integer value // wd_..._B width as a Bit #(n) value @@ -33,7 +33,7 @@ package AXI4_to_LDST; // slices_per_axi_data number of wd_ldst_data slices in wd_axi_data // szwindow NAPOT window of size specified by AWSIZE, containing AWADDR -// ================================================================ +// **************************************************************** export ldst_b, ldst_h, ldst_w, ldst_d; export AXI4_to_LDST_IFC (..); @@ -46,7 +46,7 @@ import Vector :: *; import FIFOF :: *; // ---------------- -// BSV additional libs +// Bluespec misc. libs import Cur_Cycle :: *; import Semi_FIFOF :: *; @@ -59,83 +59,71 @@ import AXI4_to_LDST_utils :: *; import AXI4_to_LD :: *; import AXI4_to_ST :: *; -// ================================================================ +// **************************************************************** // The interface for the module -interface AXI4_to_LDST_IFC #(numeric type wd_id_t, - numeric type wd_addr_t, - numeric type wd_axi_data_t, - numeric type wd_user_t, - numeric type wd_ldst_data_t); - - interface AXI4_S_IFC #(wd_id_t, wd_addr_t, wd_axi_data_t, wd_user_t) - axi4_S; - +interface AXI4_to_LDST_IFC #(numeric type wd_id, + numeric type wd_addr, + numeric type wd_axi_data, + numeric type wd_user, + numeric type wd_ldst_data); // Stores - interface FIFOF_O #(Tuple3 #(Bit #(2), // width B/H/W/D - Bit #(wd_addr_t), // addr - Bit #(wd_ldst_data_t))) // wdata + interface FIFOF_O #(Tuple3 #(Bit #(2), // width B/H/W/D + Bit #(wd_addr), // addr + Bit #(wd_ldst_data))) // wdata st_reqs; interface FIFOF_I #(Bool) // True <=> err st_rsps; // Loads - interface FIFOF_O #(Tuple2 #(Bit #(2), // width B/H/W/D - Bit #(wd_addr_t))) // addr + interface FIFOF_O #(Tuple2 #(Bit #(2), // width B/H/W/D + Bit #(wd_addr))) // addr ld_reqs; interface FIFOF_I #(Tuple2 #(Bool, // True <=> err - Bit #(wd_ldst_data_t))) // rdata + Bit #(wd_ldst_data))) // rdata ld_rsps; endinterface // ================================================================ // The module (uses separate modules, below, for Load and Store, respectively -module mkAXI4_to_LDST (AXI4_to_LDST_IFC #(wd_id_t, - wd_addr_t, - wd_axi_data_t, - wd_user_t, - wd_ldst_data_t)) +module mkAXI4_to_LDST #(AXI4_M_IFC #(wd_id, wd_addr, wd_axi_data, wd_user) ifc_M) + (AXI4_to_LDST_IFC #(wd_id, + wd_addr, + wd_axi_data, + wd_user, + wd_ldst_data)) - provisos (Add #(a__, 8, wd_addr_t), + provisos (Add #(a__, 8, wd_addr), - Mul #(wd_ldst_data_t, slices_per_axi_data_t, wd_axi_data_t), + Mul #(wd_ldst_data, slices_per_axi_data, wd_axi_data), - Mul #(wdB_axi_data_t, 8, wd_axi_data_t), + Mul #(wdB_axi_data, 8, wd_axi_data), // bsc demands this next proviso though it seems redundant // (maybe not redundant due to integer div?) - Div #(wd_axi_data_t, 8, wdB_axi_data_t), + Div #(wd_axi_data, 8, wdB_axi_data), - Mul #(wdB_ldst_data_t, 8, wd_ldst_data_t), + Mul #(wdB_ldst_data, 8, wd_ldst_data), // Redundant, but bsc doesn't work it out - Mul #(wdB_ldst_data_t, slices_per_axi_data_t, wdB_axi_data_t), + Mul #(wdB_ldst_data, slices_per_axi_data, wdB_axi_data), - Add #(b__, TLog #(TAdd #(1, wdB_ldst_data_t)), 8) + Add #(b__, TLog #(TAdd #(1, wdB_ldst_data)), 8) ); - // Transactor for axi4_S - AXI4_S_Xactor_IFC #(wd_id_t, wd_addr_t, wd_axi_data_t, wd_user_t) - axi4_S_xactor <- mkAXI4_S_Xactor; - // Store converter - AXI4_to_ST_IFC #(wd_addr_t, wd_ldst_data_t) - st_ifc <- mkAXI4_to_ST (axi4_S_xactor.o_wr_addr, - axi4_S_xactor.o_wr_data, - axi4_S_xactor.i_wr_resp); + AXI4_to_ST_IFC #(wd_addr, wd_ldst_data) + st_ifc <- mkAXI4_to_ST (ifc_M.o_AW, ifc_M.o_W, ifc_M.i_B); // Load converter - AXI4_to_LD_IFC #(wd_addr_t, wd_ldst_data_t) - ld_ifc <- mkAXI4_to_LD (axi4_S_xactor.o_rd_addr, - axi4_S_xactor.i_rd_data); + AXI4_to_LD_IFC #(wd_addr, wd_ldst_data) + ld_ifc <- mkAXI4_to_LD (ifc_M.o_AR, ifc_M.i_R); // ---------------------------------------------------------------- // INTERFACE - interface axi4_S = axi4_S_xactor.axi_side; - interface st_reqs = st_ifc.reqs; interface st_rsps = st_ifc.rsps; diff --git a/Libraries/AMBA_Fabrics/AXI4/AXI4_to_LDST_utils.bsv b/Libraries/AMBA_Fabrics/AXI4/AXI4_to_LDST_utils.bsv index 4fb6252..11da26c 100644 --- a/Libraries/AMBA_Fabrics/AXI4/AXI4_to_LDST_utils.bsv +++ b/Libraries/AMBA_Fabrics/AXI4/AXI4_to_LDST_utils.bsv @@ -1,4 +1,4 @@ -// Copyright (c) 2021 Rishiyur S. Nikhil and Bluespec, Inc. All Rights Reserved +// Copyright (c) 2021-2024 Rishiyur S. Nikhil and Bluespec, Inc. All Rights Reserved // Author: Rishiyur S. Nikhil // // SPDX-License-Identifier: BSD-3-Clause diff --git a/Libraries/AMBA_Fabrics/AXI4/AXI4_to_ST.bsv b/Libraries/AMBA_Fabrics/AXI4/AXI4_to_ST.bsv index 64072b6..95ce357 100644 --- a/Libraries/AMBA_Fabrics/AXI4/AXI4_to_ST.bsv +++ b/Libraries/AMBA_Fabrics/AXI4/AXI4_to_ST.bsv @@ -1,6 +1,5 @@ -// Copyright (c) 2021 Rishiyur S. Nikhil and Bluespec, Inc. All Rights Reserved -// Author: Rishiyur S. Nikhil -// +// Copyright (c) 2021-2024 Rishiyur S. Nikhil and Bluespec, Inc. All Rights Reserved + // SPDX-License-Identifier: BSD-3-Clause package AXI4_to_ST; @@ -22,7 +21,7 @@ import Vector :: *; import FIFOF :: *; // ---------------- -// BSV additional libs +// Bluespec misc. libs import Cur_Cycle :: *; import Semi_FIFOF :: *; @@ -67,9 +66,9 @@ Integer verbosity = 0; // Module module mkAXI4_to_ST - #(FIFOF_O #(AXI4_Wr_Addr #(wd_id_t, wd_addr_t, wd_user_t)) o_wr_addr, - FIFOF_O #(AXI4_Wr_Data #(wd_axi_data_t, wd_user_t)) o_wr_data, - FIFOF_I #(AXI4_Wr_Resp #(wd_id_t, wd_user_t)) i_wr_resp) + #(FIFOF_O #(AXI4_AW #(wd_id_t, wd_addr_t, wd_user_t)) o_wr_addr, + FIFOF_O #(AXI4_W #(wd_axi_data_t, wd_user_t)) o_wr_data, + FIFOF_I #(AXI4_B #(wd_id_t, wd_user_t)) i_wr_resp) (AXI4_to_ST_IFC #(wd_addr_t, wd_ldst_data_t)) provisos (Add #(a__, 8, wd_addr_t), @@ -160,10 +159,11 @@ module mkAXI4_to_ST Bit #(wd_addr_t) addr_axi_bus_lo = fn_addr_to_NAPOT (awaddr, fromInteger (wdB_axi_data_I)); // Bytelane of awaddr on AXI data - Bit #(8) addr_bytelane = fn_addr_to_axi_data_bytelane (awaddr, wdB_axi_data_I); + Bit #(8) addr_bytelane = fn_addr_to_axi_data_bytelane (awaddr, wdB_axi_data_I); // AWSIZE specifies a NAPOT window around awaddr, ... - Bit #(8) wdB_szwindow_B = fv_AXI4_Size_to_num_bytes (wr_addr_S.awsize); + Bit #(8) wdB_szwindow_B = fv_AXI4_Size_to_num_bytes (wr_addr_S.awsize); + // Address of NAPOT AWSIZE window containing awaddr Bit #(wd_addr_t) addr_szwindow_lo = fn_addr_to_NAPOT (awaddr, wdB_szwindow_B); @@ -311,7 +311,7 @@ module mkAXI4_to_ST rule rl_start_xaction (rg_state == STATE_IDLE); if (verbosity > 0) begin - $display ("%0d: %m.AXI4_to_ST:rl_start_xaction ================", cur_cycle); + $display ("%0d: AXI4_to_ST:rl_start_xaction ================", cur_cycle); fa_show_values(); end @@ -320,7 +320,7 @@ module mkAXI4_to_ST Bool illegal_req = False; if (wdB_szwindow_B > fromInteger (wdB_axi_data_I)) begin if (verbosity == 0) - $display ("%0d: %m.AXI4_to_ST:rl_start_xaction ================", cur_cycle); + $display ("%0d: AXI4_to_ST:rl_start_xaction ================", cur_cycle); $display (" ERROR: illegal AXI4 request"); $display (" awsize 0x%0h bytes > axi data bus width 0x%0h bytes", wdB_szwindow_B, wdB_axi_data_I); @@ -329,7 +329,7 @@ module mkAXI4_to_ST if (wr_addr_S.awlen != 0) begin if (verbosity == 0) - $display ("%0d: %m.AXI4_to_ST:rl_start_xaction ================", cur_cycle); + $display ("%0d: AXI4_to_ST:rl_start_xaction ================", cur_cycle); $display (" ERROR: illegal AXI4 request"); $display (" awlen 0x%0h; only awlen 0 (1-beat bursts) supported", wr_addr_S.awlen); @@ -338,7 +338,7 @@ module mkAXI4_to_ST if (! wr_data_S.wlast) begin if (verbosity == 0) - $display ("%0d: %m.AXI4_to_ST:rl_start_xaction ================", cur_cycle); + $display ("%0d: AXI4_to_ST:rl_start_xaction ================", cur_cycle); $display (" ERROR: illegal AXI4 request"); $display (" wlast != 1; only 1-beat bursts supported"); illegal_req = True; @@ -381,7 +381,7 @@ module mkAXI4_to_ST rule rl_next_slice (rg_state == STATE_SLICE); if (verbosity > 0) - $display ("%0d: %m.AXI4_to_ST:rl_next_slice", cur_cycle); + $display ("%0d: AXI4_to_ST:rl_next_slice", cur_cycle); Vector #(slices_per_axi_data_t, Bit #(wd_ldst_data_t)) v_slice = rg_v_slice; @@ -407,7 +407,7 @@ module mkAXI4_to_ST rule rl_partial (rg_state == STATE_PARTIAL); if (verbosity > 0) begin - $display ("%0d: %m.AXI4_to_ST:rl_partial", cur_cycle); + $display ("%0d: AXI4_to_ST:rl_partial", cur_cycle); $display (" rg_slice %0h rg_bytelane_hi..lo [%0h..%0h] rg_bytelane_slice_lo %0h", rg_slice, rg_bytelane_hi, rg_bytelane_lo, rg_bytelane_slice_lo); end @@ -442,7 +442,7 @@ module mkAXI4_to_ST rule rl_finish_req (rg_state == STATE_FINISH_REQ); if (verbosity > 0) - $display ("%0d: %m.AXI4_to_ST:rl_finish_req", cur_cycle); + $display ("%0d: AXI4_to_ST:rl_finish_req", cur_cycle); f_st_rsp_info.enq (1); // 'done' sentinel f_axi_rsp_info.enq (tuple3 (False, wr_addr_S.awid, wr_addr_S.awuser)); @@ -462,7 +462,7 @@ module mkAXI4_to_ST rg_cumulative_err <= (rg_cumulative_err || err); if (verbosity > 0) - $display ("%0d: %m.AXI4_to_ST:rl_handle_st_rsps: err = %0d", cur_cycle, err); + $display ("%0d: AXI4_to_ST:rl_handle_st_rsps: err = %0d", cur_cycle, err); endrule // ---------------- @@ -472,15 +472,15 @@ module mkAXI4_to_ST match { .illegal_req, .bid, .buser } = f_axi_rsp_info.first; f_axi_rsp_info.deq; - let wr_resp_S = AXI4_Wr_Resp {bid: bid, - bresp: ((illegal_req || rg_cumulative_err) - ? axi4_resp_slverr - : axi4_resp_okay), - buser: buser}; + let wr_resp_S = AXI4_B {bid: bid, + bresp: ((illegal_req || rg_cumulative_err) + ? axi4_resp_slverr + : axi4_resp_okay), + buser: buser}; i_wr_resp.enq (wr_resp_S); if (verbosity > 0) begin - $display ("%0d: %m.AXI4_to_ST:rl_send_axi_response", cur_cycle); + $display ("%0d: AXI4_to_ST:rl_send_axi_response", cur_cycle); $display (" ", fshow (wr_resp_S)); end endrule @@ -493,7 +493,7 @@ module mkAXI4_to_ST rule rl_illegal_req (rg_state == STATE_ILLEGAL_REQ); if (verbosity > 0) begin - $write ("%0d: %m.AXI4_to_ST:rl_illegal_req: rg_discard_count = %0h", + $write ("%0d: AXI4_to_ST:rl_illegal_req: rg_discard_count = %0h", cur_cycle, rg_discard_count); if (rg_discard_count == 0) $write (" (last)"); $display (""); diff --git a/Libraries/AMBA_Fabrics/AXI4/Makefile b/Libraries/AMBA_Fabrics/AXI4/Makefile index 8f7df88..06be1fe 100644 --- a/Libraries/AMBA_Fabrics/AXI4/Makefile +++ b/Libraries/AMBA_Fabrics/AXI4/Makefile @@ -8,28 +8,25 @@ LIBNAME=AMBA_Fabrics/AXI4 # and defines the install target include ../../common.mk -# Requires files in Misc +# Requires files in Misc and Utils BSCFLAGS += -p $(BUILDDIR)/../../Misc:+ +BSCFLAGS += -p $(BUILDDIR)/../Utils:+ .PHONY: build build: + $(BSC) -u $(BSCFLAGS) AXI4_Types.bsv + $(BSC) -u $(BSCFLAGS) AXI4_BSV_RTL.bsv + $(BSC) -u $(BSCFLAGS) AXI4_Mem_Model.bsv + $(BSC) -u $(BSCFLAGS) AXI4_to_LD.bsv + $(BSC) -u $(BSCFLAGS) AXI4_to_LDST.bsv + $(BSC) -u $(BSCFLAGS) AXI4_to_LDST_utils.bsv + $(BSC) -u $(BSCFLAGS) AXI4_to_ST.bsv $(BSC) -u $(BSCFLAGS) AXI4_Addr_Translator.bsv - $(BSC) -u $(BSCFLAGS) AXI4_ClockCrossing.bsv $(BSC) -u $(BSCFLAGS) AXI4_Clock_Crossers.bsv $(BSC) -u $(BSCFLAGS) AXI4_Deburster.bsv - $(BSC) -u $(BSCFLAGS) AXI4_Extra_Xactors.bsv $(BSC) -u $(BSCFLAGS) AXI4_Fabric.bsv $(BSC) -u $(BSCFLAGS) AXI4_Gate.bsv - $(BSC) -u $(BSCFLAGS) AXI4_Mem_Model.bsv - $(BSC) -u $(BSCFLAGS) AXI4_Types.bsv $(BSC) -u $(BSCFLAGS) AXI4_Widener.bsv - $(BSC) -u $(BSCFLAGS) AXI4_Xactors.bsv - $(BSC) -u $(BSCFLAGS) AXI4_to_LD.bsv - $(BSC) -u $(BSCFLAGS) AXI4_to_LDST.bsv - $(BSC) -u $(BSCFLAGS) AXI4_to_LDST_utils.bsv - $(BSC) -u $(BSCFLAGS) AXI4_to_ST.bsv - $(BSC) -u $(BSCFLAGS) AXI_SyncBuffer.bsv - $(BSC) -u $(BSCFLAGS) AXI4_DDR_Model.bsv .PHONY: clean full_clean clean full_clean: diff --git a/Libraries/AMBA_Fabrics/AXI4/README_AXI4.adoc b/Libraries/AMBA_Fabrics/AXI4/README_AXI4.adoc new file mode 100644 index 0000000..4aee1c2 --- /dev/null +++ b/Libraries/AMBA_Fabrics/AXI4/README_AXI4.adoc @@ -0,0 +1,176 @@ += About `bsc-contrib/Libraries/AMBA_Fabrics/AXI4` +:revnumber: v1.00 +:revdate: 2024-12-09 +:sectnums: +:imagesdir: Doc/Figs +:toc: +:toclevels: 3 +:toc-title: Contents +:keywords: Bluespec, B-Lang, BSV, BH, AMBA, ARM AXI, AXI4, AXI4-Lite, AXI4-Stream + +// ================================================================ + +Copyright (C) 2017-2023 Bluespec, Inc. All Rights Reserved + +Copyright (C) 2024 B-Lang.org. All Rights Reserved + +SPDX-License-Identifier: BSD-3-Clause + +// ================================================================ + +Pleae see README in parent directory +(link:../README_AMBA_Fabrics.adoc[adoc],link:../README_AMBA_Fabrics.html[html]) +for general introduction and about compiling/building/testing. + +This directory describes AXI4 facilities, including type definitions, +cross-bar switches, connectors, clock-crossers, and edge-transactors +to connect the B-Lang world to existing RTL (e.g., external AMBA IP). + +These source codes may import other `bsv-contrib` libraries; be sure +they are visible in your _bsc_ compiler paths: + + bsc-contrib/Libraries/AMBA_Fabrics/Utils/ + bsc-contrib/Libraries/Misc/ + +// ================================================================ +== General + +Many of these modules take an upstream `AXI4_M_IFC` and/or a +downstream `AXI4_S_IFC` as a module parameter. Thus, the actual FIFO +buffers producing these interfaces are external, allowing a choice of +buffering alternatives. + +// ================================================================ +== `AXI4_Types.bsv` + +This is a key file used by all the others. Defines: + +* Basic bus and field payload types and structs for AW, W, B, AR, R, + some help-functions and display-formatting functions + +* M and S interface types, their connections, and dummy tie-offs +* AXI4 Buffers with different kinds of internal FIFOs + +Everything is parameterized on wd_id, wd_addr, wd_data, wd_user (bit +widths of corresponding buses). + +// ================================================================ +== `AXI4_Fabric.bsv` + +NOTE: Needs _bsc_ `-aggressive-conditions` flag (else may deadlock). + +Defines a module for a crossbar switch from a vector of Ms to a vector of Ss. + +Parameterized on number of Ms and Ss (`num_M` and `num_S`) and AXI4 +bus bitwidths (`wd_id`, `wd_addr`, `wd_data`, `wd_user`). + +Also paramterized by function `fn_addr_to_S_num()` which decides which +S (if any) services which address (used to route transactions from an +M to the appropriate S). + +The vector-of-Ms and vector-of-Ss are module parameters, so all +buffering decisions are external to this module. + +Arbitration is statically decided, based on the index of an M or S in +the input vectors: + +* For simultaneously arriving requests from two Ms for a common S, the + lower-indexed M wins. + +* For simultaneously arriving responses from two Ss for a common M, + the lower-indexed S wins. + +Limitation: Corresponding field-widths are kept identical across AW, +W, B, AR and R. Technically, the `awwidth` and `arwidth` could +differ; `awid`, `bid`, `arid`, `rid` could differ; all the `user` +fields could differ, etc., but we have chosen not to make separate +parameters for all these. Please make your own modified variant of +this code if you need such capability. + +See: + + testing/bsc.contrib/AMBA_Fabrics/AXI4/Test_AXI4_Fabric.bsv + +for an example of instantiating an `AXI4_Fabric` and connecting it to Ms and Ss. + +// ================================================================ +== `AXI4_Mem_Model.bsv` + +Module that takes an M interface parameter and attaches a memory model +to it. Parameterized by module-id, base address and address limit, +and all the AXI4 bus widths. Optionally zeroes out memory after +reset. In simulation, can load a memhex file to initialize memory. + +Implements the memory using a B-Lang `RegFile`, so this is technically +synthesizable to hardware, although `RegFile` is not a good choice for +anything other than small memories. + +// ================================================================ +== Misc. + +=== `AXI4_Addr_Translator.bsv` + +Functions to transform an M interface into another M, and an S into +another S, simply adding/subtracting a given address-delta to incoming +address. + +=== `AXI4_Deburster.bsv` + +Module that attaches to S interface parameter (downstream) and itself +offers another S interface (upstream). A burst transaction from the +upstream side is converted into individual (non-burst) transactions on +the downstream side. + +=== `AXI4_Widener.bsv` + +Module which connects an M and an S interface paramter. The data +width on S is some power-of-2 multiple of the data width on M. + +=== `AXI4_Gate.bsv` + +Module which connects an M and an S interface parameter. A method in +the module interface controls whether traffic flows between M and S or +is blocked. Can be used to for authenticated access. + +// ================================================================ +== `AXI4_to_LDST.bsv` + +Module which takes an M interface (upstream), and whose interface is a +pair of FIFOs representing an simple "store" request/response +interface and a pair of FIFOs representing an ordinary "load" +request/response interface. Data on the simple interfaces are +LSB-aligned (not lane-aligned like in AXI). + +Internal help-packages: + +* `AXI4_to_LD.bsv` +* `AXI4_to_ST.bsv` +* `AXI4_to_LDST_utils.bsv` + +// ================================================================ +== `AXI4_Clock_Crossers.bsv` + +Facilities for taking an AXI4 bus across a clock- and/or reset-domain +boundary. Effectively an AXI4 buffer where the upstream and +downstream sides have different clocks and resets. + +// ================================================================ +== `AXI4_BSV_RTL.bsv` + +Defines RTL-level interfaces `AXI4_RTL_M_IFC` and `AXI4_RTL_M_IFC` +(AMBA AXI bus names, ready-valid signals), `mkConnection` to connect +them in a one-liner, dummy M and S interfaces (tie-offs), and buffers +to connect B-Lang-style interfaces to RTL-style interfaces (B-Lang M +to RTL S, and RTL B-Lang to B-Lang S). + +// ================================================================ +== Unit tests + +There are some unit tests in: + + bsc-contrib/testing/bsc.contrib/AMBA_Fabrics/AXI4/Test_*.bsv + +The conventions for unit tests are described in more detail in the +README in the parent directory +(link:../README_AMBA_Fabrics.adoc[adoc],link:../README_AMBA_Fabrics.html[html]). + +// ================================================================ diff --git a/Libraries/AMBA_Fabrics/AXI4/README_AXI4.html b/Libraries/AMBA_Fabrics/AXI4/README_AXI4.html new file mode 100644 index 0000000..8f332a7 --- /dev/null +++ b/Libraries/AMBA_Fabrics/AXI4/README_AXI4.html @@ -0,0 +1,733 @@ + + + + + + + + +About bsc-contrib/Libraries/AMBA_Fabrics/AXI4 + + + + + +
+
+
+
+

Copyright © 2017-2023 Bluespec, Inc. All Rights Reserved
+Copyright © 2024 B-Lang.org. All Rights Reserved

+
+
+

SPDX-License-Identifier: BSD-3-Clause

+
+
+

Pleae see README in parent directory +(adoc,html) +for general introduction and about compiling/building/testing.

+
+
+

This directory describes AXI4 facilities, including type definitions, +cross-bar switches, connectors, clock-crossers, and edge-transactors +to connect the B-Lang world to existing RTL (e.g., external AMBA IP).

+
+
+

These source codes may import other bsv-contrib libraries; be sure +they are visible in your bsc compiler paths:

+
+
+
+
bsc-contrib/Libraries/AMBA_Fabrics/Utils/
+bsc-contrib/Libraries/Misc/
+
+
+
+
+
+

1. General

+
+
+

Many of these modules take an upstream AXI4_M_IFC and/or a +downstream AXI4_S_IFC as a module parameter. Thus, the actual FIFO +buffers producing these interfaces are external, allowing a choice of +buffering alternatives.

+
+
+
+
+

2. AXI4_Types.bsv

+
+
+

This is a key file used by all the others. Defines:

+
+
+
    +
  • +

    Basic bus and field payload types and structs for AW, W, B, AR, R, +some help-functions and display-formatting functions

    +
  • +
  • +

    M and S interface types, their connections, and dummy tie-offs

    +
  • +
  • +

    AXI4 Buffers with different kinds of internal FIFOs

    +
  • +
+
+
+

Everything is parameterized on wd_id, wd_addr, wd_data, wd_user (bit +widths of corresponding buses).

+
+
+
+
+

3. AXI4_Fabric.bsv

+
+
+ + + + + +
+
Note
+
+Needs bsc -aggressive-conditions flag (else may deadlock). +
+
+
+

Defines a module for a crossbar switch from a vector of Ms to a vector of Ss.

+
+
+

Parameterized on number of Ms and Ss (num_M and num_S) and AXI4 +bus bitwidths (wd_id, wd_addr, wd_data, wd_user).

+
+
+

Also paramterized by function fn_addr_to_S_num() which decides which +S (if any) services which address (used to route transactions from an +M to the appropriate S).

+
+
+

The vector-of-Ms and vector-of-Ss are module parameters, so all +buffering decisions are external to this module.

+
+
+

Arbitration is statically decided, based on the index of an M or S in +the input vectors:

+
+
+
    +
  • +

    For simultaneously arriving requests from two Ms for a common S, the +lower-indexed M wins.

    +
  • +
  • +

    For simultaneously arriving responses from two Ss for a common M, +the lower-indexed S wins.

    +
  • +
+
+
+

Limitation: Corresponding field-widths are kept identical across AW, +W, B, AR and R. Technically, the awwidth and arwidth could +differ; awid, bid, arid, rid could differ; all the user +fields could differ, etc., but we have chosen not to make separate +parameters for all these. Please make your own modified variant of +this code if you need such capability.

+
+
+

See:

+
+
+
+
testing/bsc.contrib/AMBA_Fabrics/AXI4/Test_AXI4_Fabric.bsv
+
+
+
+

for an example of instantiating an AXI4_Fabric and connecting it to Ms and Ss.

+
+
+
+
+

4. AXI4_Mem_Model.bsv

+
+
+

Module that takes an M interface parameter and attaches a memory model +to it. Parameterized by module-id, base address and address limit, +and all the AXI4 bus widths. Optionally zeroes out memory after +reset. In simulation, can load a memhex file to initialize memory.

+
+
+

Implements the memory using a B-Lang RegFile, so this is technically +synthesizable to hardware, although RegFile is not a good choice for +anything other than small memories.

+
+
+
+
+

5. Misc.

+
+
+

5.1. AXI4_Addr_Translator.bsv

+
+

Functions to transform an M interface into another M, and an S into +another S, simply adding/subtracting a given address-delta to incoming +address.

+
+
+
+

5.2. AXI4_Deburster.bsv

+
+

Module that attaches to S interface parameter (downstream) and itself +offers another S interface (upstream). A burst transaction from the +upstream side is converted into individual (non-burst) transactions on +the downstream side.

+
+
+
+

5.3. AXI4_Widener.bsv

+
+

Module which connects an M and an S interface paramter. The data +width on S is some power-of-2 multiple of the data width on M.

+
+
+
+

5.4. AXI4_Gate.bsv

+
+

Module which connects an M and an S interface parameter. A method in +the module interface controls whether traffic flows between M and S or +is blocked. Can be used to for authenticated access.

+
+
+
+
+
+

6. AXI4_to_LDST.bsv

+
+
+

Module which takes an M interface (upstream), and whose interface is a +pair of FIFOs representing an simple "store" request/response +interface and a pair of FIFOs representing an ordinary "load" +request/response interface. Data on the simple interfaces are +LSB-aligned (not lane-aligned like in AXI).

+
+
+

Internal help-packages:

+
+
+
    +
  • +

    AXI4_to_LD.bsv

    +
  • +
  • +

    AXI4_to_ST.bsv

    +
  • +
  • +

    AXI4_to_LDST_utils.bsv

    +
  • +
+
+
+
+
+

7. AXI4_Clock_Crossers.bsv

+
+
+

Facilities for taking an AXI4 bus across a clock- and/or reset-domain +boundary. Effectively an AXI4 buffer where the upstream and +downstream sides have different clocks and resets.

+
+
+
+
+

8. AXI4_BSV_RTL.bsv

+
+
+

Defines RTL-level interfaces AXI4_RTL_M_IFC and AXI4_RTL_M_IFC +(AMBA AXI bus names, ready-valid signals), mkConnection to connect +them in a one-liner, dummy M and S interfaces (tie-offs), and buffers +to connect B-Lang-style interfaces to RTL-style interfaces (B-Lang M +to RTL S, and RTL B-Lang to B-Lang S).

+
+
+
+
+

9. Unit tests

+
+
+

There are some unit tests in:

+
+
+
+
bsc-contrib/testing/bsc.contrib/AMBA_Fabrics/AXI4/Test_*.bsv
+
+
+
+

The conventions for unit tests are described in more detail in the +README in the parent directory +(adoc,html).

+
+
+
+
+ + + \ No newline at end of file diff --git a/Libraries/AMBA_Fabrics/AXI4_Lite/AXI4L_Clock_Crossers.bsv b/Libraries/AMBA_Fabrics/AXI4_Lite/AXI4L_Clock_Crossers.bsv index 4992ec0..895d672 100644 --- a/Libraries/AMBA_Fabrics/AXI4_Lite/AXI4L_Clock_Crossers.bsv +++ b/Libraries/AMBA_Fabrics/AXI4_Lite/AXI4L_Clock_Crossers.bsv @@ -33,7 +33,7 @@ import Clocks :: *; import Connectable :: *; // ---------------- -// BSV additional libs +// Bluespec misc. libs import Cur_Cycle :: *; import GetPut_Aux :: *; @@ -42,7 +42,7 @@ import Semi_FIFOF :: *; // ================================================================ // Project imports -import AXI_SyncBuffer :: *; +import AXIx_SyncBuffer :: *; import AXI4L_Types :: *; import AXI4L_Xactors :: *; @@ -64,12 +64,12 @@ module mkAXI4L_S_Clock_Crosser axi4L_M_xactor <- mkAXI4L_M_Xactor (clocked_by clk1, reset_by rst1); // Syncbuffer between transactors - AXI_SyncBuffer_IFC #(AXI4L_Wr_Addr #(wd_addr, wd_user), - AXI4L_Wr_Data #(wd_data), - AXI4L_Wr_Resp #(wd_user), - AXI4L_Rd_Addr #(wd_addr, wd_user), - AXI4L_Rd_Data #(wd_data, wd_user)) - axi4L_syncbuf <- mkAXI_SyncBuffer (depth, clk2, rst2, clk1, rst1); + AXIx_SyncBuffer_IFC #(AXI4L_Wr_Addr #(wd_addr, wd_user), + AXI4L_Wr_Data #(wd_data), + AXI4L_Wr_Resp #(wd_user), + AXI4L_Rd_Addr #(wd_addr, wd_user), + AXI4L_Rd_Data #(wd_data, wd_user)) + axi4L_syncbuf <- mkAXIx_SyncBuffer (depth, clk2, rst2, clk1, rst1); // Transactor with ifc2 AXI4L_S_Xactor_IFC #(wd_addr, wd_data, wd_user) @@ -115,12 +115,12 @@ module mkAXI4L_M_Clock_Crosser axi4L_S_xactor <- mkAXI4L_S_Xactor (clocked_by clk1, reset_by rst1); // Syncbuffer between transactors - AXI_SyncBuffer_IFC #(AXI4L_Wr_Addr #(wd_addr, wd_user), - AXI4L_Wr_Data #(wd_data), - AXI4L_Wr_Resp #(wd_user), - AXI4L_Rd_Addr #(wd_addr, wd_user), - AXI4L_Rd_Data #(wd_data, wd_user)) - axi4L_syncbuf <- mkAXI_SyncBuffer (depth, clk1, rst1, clk2, rst2); + AXIx_SyncBuffer_IFC #(AXI4L_Wr_Addr #(wd_addr, wd_user), + AXI4L_Wr_Data #(wd_data), + AXI4L_Wr_Resp #(wd_user), + AXI4L_Rd_Addr #(wd_addr, wd_user), + AXI4L_Rd_Data #(wd_data, wd_user)) + axi4L_syncbuf <- mkAXIx_SyncBuffer (depth, clk1, rst1, clk2, rst2); // Transactor with ifc2 AXI4L_M_Xactor_IFC #(wd_addr, wd_data, wd_user) diff --git a/Libraries/AMBA_Fabrics/AXI4_Lite/AXI4L_Fabric.bsv b/Libraries/AMBA_Fabrics/AXI4_Lite/AXI4L_Fabric.bsv index b7a7cf0..a1c2122 100644 --- a/Libraries/AMBA_Fabrics/AXI4_Lite/AXI4L_Fabric.bsv +++ b/Libraries/AMBA_Fabrics/AXI4_Lite/AXI4L_Fabric.bsv @@ -17,7 +17,7 @@ import SpecialFIFOs :: *; import ConfigReg :: *; // ---------------- -// BSV additional libs +// Bluespec misc. libs import Cur_Cycle :: *; diff --git a/Libraries/AMBA_Fabrics/AXI4_Lite/AXI4L_Gate.bsv b/Libraries/AMBA_Fabrics/AXI4_Lite/AXI4L_Gate.bsv index a9e1bcc..1852d6b 100644 --- a/Libraries/AMBA_Fabrics/AXI4_Lite/AXI4L_Gate.bsv +++ b/Libraries/AMBA_Fabrics/AXI4_Lite/AXI4L_Gate.bsv @@ -21,7 +21,7 @@ import Vector :: *; import FIFOF :: *; // ---------------- -// BSV additional libs +// Bluespec misc. libs import Cur_Cycle :: *; import GetPut_Aux :: *; @@ -111,8 +111,7 @@ module mkAXI4L_Gate $display ("WARNING: rl_wr_addr_disabled: rec'd wr request from M when gate disabled."); $display (" ", fshow (wra)); - $display (" Returning error response."); - $display (" %0d: %m", cur_cycle); + $display (" %0d: Returning error response.", cur_cycle); endrule rule rl_wr_data_disabled (respond_with_err && (! rg_enabled)); @@ -123,8 +122,7 @@ module mkAXI4L_Gate rule rl_wr_resp_disabled_drain_S (respond_with_err && (! rg_enabled)); let wrr <- pop_o (xactor_to_S.o_wr_resp); $display ("WARNING: rl_wr_resp_disabled: rec'd wr resp from S when gate disabled; ignoring"); - $display (" (there couldn't have been a request)"); - $display (" %0d: %m", cur_cycle); + $display (" %0d: (there couldn't have been a request)", cur_cycle); endrule rule rl_rd_addr_disabled (respond_with_err && (! rg_enabled)); @@ -136,15 +134,13 @@ module mkAXI4L_Gate $display ("WARNING: rl_rd_addr_disabled: rec'd rd request from M when gate disabled."); $display (" ", fshow (rda)); - $display (" Returning error response."); - $display (" %0d: %m", cur_cycle); + $display (" %0d: Returning error response.", cur_cycle); endrule rule rl_rd_data_disabled_drain_S (respond_with_err && (! rg_enabled)); let rdd <- pop_o (xactor_to_S.o_rd_data); $display ("WARNING: rl_rd_data_disabled: rec'd rd resp from S when gate disabled; ignoring"); - $display (" (there couldn't have been a request)"); - $display (" %0d: %m", cur_cycle); + $display (" %0d: (there couldn't have been a request)", cur_cycle); endrule // ---------------------------------------------------------------- @@ -155,9 +151,9 @@ module mkAXI4L_Gate method Action m_enable (Bool enabled); if (enabled && (! rg_enabled) && (verbosity != 0)) - $display ("%0d: %m: AXI4L ENABLING", cur_cycle); + $display ("%0d: AXI4L ENABLING", cur_cycle); else if ((! enabled) && rg_enabled && (verbosity != 0)) - $display ("%0d: %m: AXI4L DISABLING", cur_cycle); + $display ("%0d: AXI4L DISABLING", cur_cycle); rg_enabled <= enabled; endmethod diff --git a/Libraries/AMBA_Fabrics/AXI4_Lite/AXI4L_Types.bsv b/Libraries/AMBA_Fabrics/AXI4_Lite/AXI4L_Types.bsv index a1a2213..e90ccc0 100644 --- a/Libraries/AMBA_Fabrics/AXI4_Lite/AXI4L_Types.bsv +++ b/Libraries/AMBA_Fabrics/AXI4_Lite/AXI4L_Types.bsv @@ -71,7 +71,7 @@ import Connectable :: *; import BUtils :: *; // ---------------- -// BSV additional libs +// Bluespec misc. libs import Semi_FIFOF :: *; import EdgeFIFOFs :: *; diff --git a/Libraries/AMBA_Fabrics/AXI4_Lite/AXI4L_Xactors.bsv b/Libraries/AMBA_Fabrics/AXI4_Lite/AXI4L_Xactors.bsv index 0cc0b32..7b885b4 100644 --- a/Libraries/AMBA_Fabrics/AXI4_Lite/AXI4L_Xactors.bsv +++ b/Libraries/AMBA_Fabrics/AXI4_Lite/AXI4L_Xactors.bsv @@ -20,7 +20,7 @@ package AXI4L_Xactors; // None // ---------------- -// BSV additional libs +// Bluespec misc. libs import Semi_FIFOF :: *; diff --git a/Libraries/AMBA_Fabrics/AXI4_Lite/Makefile b/Libraries/AMBA_Fabrics/AXI4_Lite/Makefile index c81dd29..fda37ce 100644 --- a/Libraries/AMBA_Fabrics/AXI4_Lite/Makefile +++ b/Libraries/AMBA_Fabrics/AXI4_Lite/Makefile @@ -8,8 +8,9 @@ LIBNAME=AMBA_Fabrics/AXI4_Lite # and defines the install target include ../../common.mk -# Requires files in Misc and AMBA_Fabrics/AXI4 -BSCFLAGS += -p $(BUILDDIR)/../../Misc:$(BUILDDIR)/../../AMBA_Fabrics/AXI4:+ +# Requires files in Misc and Utils +BSCFLAGS += -p $(BUILDDIR)/../../Misc:+ +BSCFLAGS += -p $(BUILDDIR)/../Utils:+ .PHONY: build build: diff --git a/Libraries/AMBA_Fabrics/AXI4_Lite/README_AXI4L.adoc b/Libraries/AMBA_Fabrics/AXI4_Lite/README_AXI4L.adoc new file mode 100644 index 0000000..c8ef8e1 --- /dev/null +++ b/Libraries/AMBA_Fabrics/AXI4_Lite/README_AXI4L.adoc @@ -0,0 +1,105 @@ += About `bsc-contrib/Libraries/AMBA_Fabrics/AXI4_Lite` +:revnumber: v1.00 +:revdate: 2024-12-07 +:sectnums: +:imagesdir: ../Doc/Figs +:toc: +:toclevels: 3 +:toc-title: Contents +:keywords: Bluespec, B-Lang, BSV, BH, AMBA, ARM AXI, AXI4, AXI4-Lite, AXI4-Stream + +// ================================================================ + +Copyright (C) 2017-2023 Bluespec, Inc. All Rights Reserved + +Copyright (C) 2024-2025 B-Lang.org. All Rights Reserved + +SPDX-License-Identifier: BSD-3-Clause + +// ================================================================ + +Pleae see README in parent directory +(link:../README_AMBA_Fabrics.adoc[adoc],link:../README_AMBA_Fabrics.html[html]) +for general introduction and about compiling/building/testing. + +This directory describes AXI4-Lite facilities, including type definitions, +cross-bar switches, connectors, clock-crossers, and edge-transactors +to connect the B-Lang world to existing RTL (e.g., external AMBA IP). + +These source codes may import other `bsv-contrib` libraries; be sure +they are visible in your _bsc_ compiler paths: + + bsc-contrib/Libraries/AMBA_Fabrics/Utils/ + bsc-contrib/Libraries/Misc/ + +// ================================================================ + +image::IMG_Under_Construction.png[align="left", width=100] + +The packages in this directory can be used as-is, but are expected to +be restructured to be more like their AXI4 siblings (more consistent +naming, clean separation of B-Lang-style and RTL-style, etc.) + +This documentation will be updated after that restructuring. + +// ================================================================ +== General + +Many of these modules take an upstream `AXI4L_M_IFC` and/or a +downstream `AXI4L_S_IFC` as a module parameter. Thus, the actual FIFO +buffers producing these interfaces are external, allowing a choice of +buffering alternatives. + +All these have a 'user' field which is not standard in AXI4-Lite (same +role as 'user' in AXI4), which can be left unused, and/or set to width +0. + +// ================================================================ +== `AXI4_Lite_Types.bsv` + +Definitions for AXI4_Lite bus types, M and S interfaces, connections +between Ms and Ss, dummy M and S tie-offs, and transactors to provide +higher-level FIFO-like interfaces to drive Ms and Ss. + +Everything is parameterizd on width of address, data and user buses. + +Note: some aspects of these definitions may seem a bit verbose and +messy; that is not typical of BSV code, but is true here because it is +meant to interface to hand-written Verilog, so we need to provide +precise control on interface signal names and protocols that are +required by the Verilog side. Pure BSV code can be an order of +magnitude more compact. + +Everything is parameterized on wd_addr, wd_data, wd_user. + +== `AXI4_Lite_Fabric.bsv` + +NOTE: Needs _bsc_ `-aggressive-conditions` flag (else will deadlock). + +Definition for interface and module for an num_M x num_S crossbar +switch with AXI4-Lite interfaces. + +This is also an example of how, within BSV code, we don't worry about +the details of AXI4-Lite signalling. We just instantiate the +transactors defined in AXI4_Lite_Types.bsv, and then work only with +simple, FIFO-like interfaces. + +Everything is parameterized on num_M, num_S, wd_addr, wd_data, +wd_user. + +// ================================================================ +== Clock-crossers, other transactors + +(... To be written ...) + +// ================================================================ +== Unit tests + +There are some unit tests in: + + bsc-contrib/testing/bsc.contrib/AMBA_Fabrics/AXI4_Lite/Test_*.bsv + +The conventions for unit tests are described in more detail in the +README in the parent directory +(link:../README_AMBA_Fabrics.adoc[adoc],link:../README_AMBA_Fabrics.html[html]). + +// ================================================================ diff --git a/Libraries/AMBA_Fabrics/AXI4_Lite/README_AXI4L.html b/Libraries/AMBA_Fabrics/AXI4_Lite/README_AXI4L.html new file mode 100644 index 0000000..37e9765 --- /dev/null +++ b/Libraries/AMBA_Fabrics/AXI4_Lite/README_AXI4L.html @@ -0,0 +1,606 @@ + + + + + + + + +About bsc-contrib/Libraries/AMBA_Fabrics/AXI4_Lite + + + + + +
+
+
+
+

Copyright © 2017-2023 Bluespec, Inc. All Rights Reserved
+Copyright © 2024-2025 B-Lang.org. All Rights Reserved

+
+
+

SPDX-License-Identifier: BSD-3-Clause

+
+
+

Pleae see README in parent directory +(adoc,html) +for general introduction and about compiling/building/testing.

+
+
+

This directory describes AXI4-Lite facilities, including type definitions, +cross-bar switches, connectors, clock-crossers, and edge-transactors +to connect the B-Lang world to existing RTL (e.g., external AMBA IP).

+
+
+

These source codes may import other bsv-contrib libraries; be sure +they are visible in your bsc compiler paths:

+
+
+
+
bsc-contrib/Libraries/AMBA_Fabrics/Utils/
+bsc-contrib/Libraries/Misc/
+
+
+
+
+IMG Under Construction +
+
+
+

The packages in this directory can be used as-is, but are expected to +be restructured to be more like their AXI4 siblings (more consistent +naming, clean separation of B-Lang-style and RTL-style, etc.)

+
+
+

This documentation will be updated after that restructuring.

+
+
+
+
+

1. General

+
+
+

Many of these modules take an upstream AXI4L_M_IFC and/or a +downstream AXI4L_S_IFC as a module parameter. Thus, the actual FIFO +buffers producing these interfaces are external, allowing a choice of +buffering alternatives.

+
+
+

All these have a 'user' field which is not standard in AXI4-Lite (same +role as 'user' in AXI4), which can be left unused, and/or set to width +0.

+
+
+
+
+

2. AXI4_Lite_Types.bsv

+
+
+

Definitions for AXI4_Lite bus types, M and S interfaces, connections +between Ms and Ss, dummy M and S tie-offs, and transactors to provide +higher-level FIFO-like interfaces to drive Ms and Ss.

+
+
+

Everything is parameterizd on width of address, data and user buses.

+
+
+

Note: some aspects of these definitions may seem a bit verbose and +messy; that is not typical of BSV code, but is true here because it is +meant to interface to hand-written Verilog, so we need to provide +precise control on interface signal names and protocols that are +required by the Verilog side. Pure BSV code can be an order of +magnitude more compact.

+
+
+

Everything is parameterized on wd_addr, wd_data, wd_user.

+
+
+
+
+

3. AXI4_Lite_Fabric.bsv

+
+
+ + + + + +
+
Note
+
+Needs bsc -aggressive-conditions flag (else will deadlock). +
+
+
+

Definition for interface and module for an num_M x num_S crossbar +switch with AXI4-Lite interfaces.

+
+
+

This is also an example of how, within BSV code, we don’t worry about +the details of AXI4-Lite signalling. We just instantiate the +transactors defined in AXI4_Lite_Types.bsv, and then work only with +simple, FIFO-like interfaces.

+
+
+

Everything is parameterized on num_M, num_S, wd_addr, wd_data, +wd_user.

+
+
+
+
+

4. Clock-crossers, other transactors

+
+
+

(…​ To be written …​)

+
+
+
+
+

5. Unit tests

+
+
+

There are some unit tests in:

+
+
+
+
bsc-contrib/testing/bsc.contrib/AMBA_Fabrics/AXI4_Lite/Test_*.bsv
+
+
+
+

The conventions for unit tests are described in more detail in the +README in the parent directory +(adoc,html).

+
+
+
+
+ + + \ No newline at end of file diff --git a/Libraries/AMBA_Fabrics/AXI4_Stream/AXI4_Stream.bsv b/Libraries/AMBA_Fabrics/AXI4_Stream/AXI4_Stream.bsv index 3087585..933b090 100644 --- a/Libraries/AMBA_Fabrics/AXI4_Stream/AXI4_Stream.bsv +++ b/Libraries/AMBA_Fabrics/AXI4_Stream/AXI4_Stream.bsv @@ -11,7 +11,7 @@ import FIFOF :: *; import Connectable :: *; // ---------------- -// BSV additional libs +// Bluespec misc. libs import Semi_FIFOF :: *; import EdgeFIFOFs :: *; @@ -303,477 +303,6 @@ module mkAXI4_Stream_S_Xactor (AXI4_Stream_S_Xactor_IFC #(wd_id, wd_dest, wd_dat interface o_stream = to_FIFOF_O (f_data); endmodule: mkAXI4_Stream_S_Xactor -/* -// ---------------------------------------------------------------- -// M transactor -// This version uses crgs and regs instead of FIFOFs. -// This uses 1/2 the resources, but introduces scheduling dependencies. - -module mkAXI4_M_Xactor_2 (AXI4_M_Xactor_IFC #(wd_id, wd_dest, wd_data, wd_user)); - - // Each crg_full, rg_data pair below represents a 1-element fifo. - - Array #(Reg #(Bool)) crg_wr_addr_full <- mkCReg (3, False); - Reg #(AXI4_Wr_Addr #(wd_id, wd_dest, wd_user)) rg_wr_addr <- mkRegU; - - Array #(Reg #(Bool)) crg_wr_data_full <- mkCReg (3, False); - Reg #(AXI4_Wr_Data #(wd_id, wd_data, wd_user)) rg_wr_data <- mkRegU; - - Array #(Reg #(Bool)) crg_wr_resp_full <- mkCReg (3, False); - Reg #(AXI4_Wr_Resp #(wd_id, wd_user)) rg_wr_resp <- mkRegU; - - Array #(Reg #(Bool)) crg_rd_addr_full <- mkCReg (3, False); - Reg #(AXI4_Rd_Addr #(wd_id, wd_dest, wd_user)) rg_rd_addr <- mkRegU; - - Array #(Reg #(Bool)) crg_rd_data_full <- mkCReg (3, False); - Reg #(AXI4_Rd_Data #(wd_id, wd_data, wd_user)) rg_rd_data <- mkRegU; - - // The following CReg port indexes specify the relative scheduling of: - // {first,deq,notEmpty} {enq,notFull} clear - - // TODO: 'deq/enq/clear = 1/2/0' is unusual, but eliminates a - // scheduling cycle in Piccolo's DCache. Normally should be 0/1/2. - - Integer port_deq = 1; - Integer port_enq = 2; - Integer port_clear = 0; - - // ---------------------------------------------------------------- - // INTERFACE - - method Action reset; - crg_wr_addr_full [port_clear] <= False; - crg_wr_data_full [port_clear] <= False; - crg_wr_resp_full [port_clear] <= False; - crg_rd_addr_full [port_clear] <= False; - crg_rd_data_full [port_clear] <= False; - endmethod - - // AXI side - interface axi_side = interface AXI4_M_IFC; - // Wr Addr channel - method Bool m_awvalid = crg_wr_addr_full [port_deq]; - method Bit #(wd_id) m_awid = rg_wr_addr.awid; - method Bit #(wd_dest) m_awaddr = rg_wr_addr.awaddr; - method Bit #(8) m_awlen = rg_wr_addr.awlen; - method AXI4_Size m_awsize = rg_wr_addr.awsize; - method Bit #(2) m_awburst = rg_wr_addr.awburst; - method Bit #(1) m_awlock = rg_wr_addr.awlock; - method Bit #(4) m_awcache = rg_wr_addr.awcache; - method Bit #(3) m_awprot = rg_wr_addr.awprot; - method Bit #(4) m_awqos = rg_wr_addr.awqos; - method Bit #(4) m_awregion = rg_wr_addr.awregion; - method Bit #(wd_user) m_awuser = rg_wr_addr.awuser; - method Action m_awready (Bool awready); - if (crg_wr_addr_full [port_deq] && awready) - crg_wr_addr_full [port_deq] <= False; // deq - endmethod - - // Wr Data channel - method Bool m_wvalid = crg_wr_data_full [port_deq]; - method Bit #(wd_id) m_wid = rg_wr_data.wid; - method Bit #(wd_data) m_wdata = rg_wr_data.wdata; - method Bit #(TDiv #(wd_data, 8)) m_wstrb = rg_wr_data.wstrb; - method Bool m_wlast = rg_wr_data.wlast; - method Bit #(wd_user) m_wuser = rg_wr_data.wuser; - method Action m_wready (Bool wready); - if (crg_wr_data_full [port_deq] && wready) - crg_wr_data_full [port_deq] <= False; - endmethod - - // Wr Response channel - method Action m_bvalid (Bool bvalid, - Bit #(wd_id) bid, - Bit #(2) bresp, - Bit #(wd_user) buser); - if (bvalid && (! (crg_wr_resp_full [port_enq]))) begin - crg_wr_resp_full [port_enq] <= True; - rg_wr_resp <= AXI4_Wr_Resp {bid: bid, - bresp: bresp, - buser: buser}; - end - endmethod - - method Bool m_bready; - return (! (crg_wr_resp_full [port_enq])); - endmethod - - // Rd Addr channel - method Bool m_arvalid = crg_rd_addr_full [port_deq]; - method Bit #(wd_id) m_arid = rg_rd_addr.arid; - method Bit #(wd_dest) m_araddr = rg_rd_addr.araddr; - method Bit #(8) m_arlen = rg_rd_addr.arlen; - method AXI4_Size m_arsize = rg_rd_addr.arsize; - method Bit #(2) m_arburst = rg_rd_addr.arburst; - method Bit #(1) m_arlock = rg_rd_addr.arlock; - method Bit #(4) m_arcache = rg_rd_addr.arcache; - method Bit #(3) m_arprot = rg_rd_addr.arprot; - method Bit #(4) m_arqos = rg_rd_addr.arqos; - method Bit #(4) m_arregion = rg_rd_addr.arregion; - method Bit #(wd_user) m_aruser = rg_rd_addr.aruser; - method Action m_arready (Bool arready); - if (crg_rd_addr_full [port_deq] && arready) - crg_rd_addr_full [port_deq] <= False; // deq - endmethod - - // Rd Data channel - method Action m_rvalid (Bool rvalid, - Bit #(wd_id) rid, - Bit #(wd_data) rdata, - Bit #(2) rresp, - Bool rlast, - Bit #(wd_user) ruser); - if (rvalid && (! (crg_rd_data_full [port_enq]))) - crg_rd_data_full [port_enq] <= True; - rg_rd_data <= (AXI4_Rd_Data {rid: rid, - rdata: rdata, - rresp: rresp, - rlast: rlast, - ruser: ruser}); - endmethod - - method Bool m_rready; - return (! (crg_rd_data_full [port_enq])); - endmethod - - endinterface; - - // FIFOF side - interface i_wr_addr = fn_crg_and_rg_to_FIFOF_I (crg_wr_addr_full [port_enq], rg_wr_addr); - interface i_wr_data = fn_crg_and_rg_to_FIFOF_I (crg_wr_data_full [port_enq], rg_wr_data); - interface o_wr_resp = fn_crg_and_rg_to_FIFOF_O (crg_wr_resp_full [port_deq], rg_wr_resp); - - interface i_rd_addr = fn_crg_and_rg_to_FIFOF_I (crg_rd_addr_full [port_enq], rg_rd_addr); - interface o_rd_data = fn_crg_and_rg_to_FIFOF_O (crg_rd_data_full [port_deq], rg_rd_data); -endmodule: mkAXI4_M_Xactor_2 - -// ================================================================ -// S transactor interface - -interface AXI4_S_Xactor_IFC #(numeric type wd_id, - numeric type wd_dest, - numeric type wd_data, - numeric type wd_user); - method Action reset; - - // AXI side - interface AXI4_S_IFC #(wd_id, wd_dest, wd_data, wd_user) axi_side; - - // FIFOF side - interface FIFOF_O #(AXI4_Wr_Addr #(wd_id, wd_dest, wd_user)) o_wr_addr; - interface FIFOF_O #(AXI4_Wr_Data #(wd_id, wd_data, wd_user)) o_wr_data; - interface FIFOF_I #(AXI4_Wr_Resp #(wd_id, wd_user)) i_wr_resp; - - interface FIFOF_O #(AXI4_Rd_Addr #(wd_id, wd_dest, wd_user)) o_rd_addr; - interface FIFOF_I #(AXI4_Rd_Data #(wd_id, wd_data, wd_user)) i_rd_data; -endinterface: AXI4_S_Xactor_IFC - -// ---------------------------------------------------------------- -// S transactor -// This version uses FIFOFs for total decoupling. - -module mkAXI4_S_Xactor (AXI4_S_Xactor_IFC #(wd_id, wd_dest, wd_data, wd_user)); - - Bool unguarded = True; - Bool guarded = False; - - // These FIFOs are guarded on BSV side, unguarded on AXI side - FIFOF #(AXI4_Wr_Addr #(wd_id, wd_dest, wd_user)) f_wr_addr <- mkGFIFOF (unguarded, guarded); - FIFOF #(AXI4_Wr_Data #(wd_id, wd_data, wd_user)) f_wr_data <- mkGFIFOF (unguarded, guarded); - FIFOF #(AXI4_Wr_Resp #(wd_id, wd_user)) f_wr_resp <- mkGFIFOF (guarded, unguarded); - - FIFOF #(AXI4_Rd_Addr #(wd_id, wd_dest, wd_user)) f_rd_addr <- mkGFIFOF (unguarded, guarded); - FIFOF #(AXI4_Rd_Data #(wd_id, wd_data, wd_user)) f_rd_data <- mkGFIFOF (guarded, unguarded); - - // ---------------------------------------------------------------- - // INTERFACE - - method Action reset; - f_wr_addr.clear; - f_wr_data.clear; - f_wr_resp.clear; - f_rd_addr.clear; - f_rd_data.clear; - endmethod - // AXI side - interface axi_side = interface AXI4_S_IFC; - // Wr Addr channel - method Action m_awvalid (Bool awvalid, - Bit #(wd_id) awid, - Bit #(wd_dest) awaddr, - Bit #(8) awlen, - AXI4_Size awsize, - Bit #(2) awburst, - Bit #(1) awlock, - Bit #(4) awcache, - Bit #(3) awprot, - Bit #(4) awqos, - Bit #(4) awregion, - Bit #(wd_user) awuser); - if (awvalid && f_wr_addr.notFull) - f_wr_addr.enq (AXI4_Wr_Addr {awid: awid, - awaddr: awaddr, - awlen: awlen, - awsize: awsize, - awburst: awburst, - awlock: awlock, - awcache: awcache, - awprot: awprot, - awqos: awqos, - awregion: awregion, - awuser: awuser}); - endmethod - - method Bool m_awready; - return f_wr_addr.notFull; - endmethod - - // Wr Data channel - method Action m_wvalid (Bool wvalid, - Bit #(wd_id) wid, - Bit #(wd_data) wdata, - it #(TDiv #(wd_data, 8)) wstrb, - Bool wlast, - Bit #(wd_user) wuser); - if (wvalid && f_wr_data.notFull) - f_wr_data.enq (AXI4_Wr_Data {wid: wid, - wdata: wdata, - wstrb: wstrb, - wlast: wlast, - wuser: wuser}); - endmethod - - method Bool m_wready; - return f_wr_data.notFull; - endmethod - - // Wr Response channel - method Bool m_bvalid = f_wr_resp.notEmpty; - method Bit #(wd_id) m_bid = f_wr_resp.first.bid; - method Bit #(2) m_bresp = f_wr_resp.first.bresp; - method Bit #(wd_user) m_buser = f_wr_resp.first.buser; - method Action m_bready (Bool bready); - if (bready && f_wr_resp.notEmpty) - f_wr_resp.deq; - endmethod - - // Rd Addr channel - method Action m_arvalid (Bool arvalid, - Bit #(wd_id) arid, - Bit #(wd_dest) araddr, - Bit #(8) arlen, - AXI4_Size arsize, - Bit #(2) arburst, - Bit #(1) arlock, - Bit #(4) arcache, - Bit #(3) arprot, - Bit #(4) arqos, - Bit #(4) arregion, - Bit #(wd_user) aruser); - if (arvalid && f_rd_addr.notFull) - f_rd_addr.enq (AXI4_Rd_Addr {arid: arid, - araddr: araddr, - arlen: arlen, - arsize: arsize, - arburst: arburst, - arlock: arlock, - arcache: arcache, - arprot: arprot, - arqos: arqos, - arregion: arregion, - aruser: aruser}); - endmethod - - method Bool m_arready; - return f_rd_addr.notFull; - endmethod - - // Rd Data channel - method Bool m_rvalid = f_rd_data.notEmpty; - method Bit #(wd_id) m_rid = f_rd_data.first.rid; - method Bit #(wd_data) m_rdata = f_rd_data.first.rdata; - method Bit #(2) m_rresp = f_rd_data.first.rresp; - method Bool m_rlast = f_rd_data.first.rlast; - method Bit #(wd_user) m_ruser = f_rd_data.first.ruser; - method Action m_rready (Bool rready); - if (rready && f_rd_data.notEmpty) - f_rd_data.deq; - endmethod - endinterface; - - // FIFOF side - interface o_wr_addr = to_FIFOF_O (f_wr_addr); - interface o_wr_data = to_FIFOF_O (f_wr_data); - interface i_wr_resp = to_FIFOF_I (f_wr_resp); - - interface o_rd_addr = to_FIFOF_O (f_rd_addr); - interface i_rd_data = to_FIFOF_I (f_rd_data); -endmodule: mkAXI4_S_Xactor - -// ---------------------------------------------------------------- -// S transactor -// This version uses crgs and regs instead of FIFOFs. -// This uses 1/2 the resources, but introduces scheduling dependencies. - -module mkAXI4_S_Xactor_2 (AXI4_S_Xactor_IFC #(wd_id, wd_dest, wd_data, wd_user)); - - // Each crg_full, rg_data pair below represents a 1-element fifo. - - // These FIFOs are guarded on BSV side, unguarded on AXI side - Array #(Reg #(Bool)) crg_wr_addr_full <- mkCReg (3, False); - Reg #(AXI4_Wr_Addr #(wd_id, wd_dest, wd_user)) rg_wr_addr <- mkRegU; - - Array #(Reg #(Bool)) crg_wr_data_full <- mkCReg (3, False); - Reg #(AXI4_Wr_Data #(wd_id, wd_data, wd_user)) rg_wr_data <- mkRegU; - - Array #(Reg #(Bool)) crg_wr_resp_full <- mkCReg (3, False); - Reg #(AXI4_Wr_Resp #(wd_id, wd_user)) rg_wr_resp <- mkRegU; - - Array #(Reg #(Bool)) crg_rd_addr_full <- mkCReg (3, False); - Reg #(AXI4_Rd_Addr #(wd_id, wd_dest, wd_user)) rg_rd_addr <- mkRegU; - - Array #(Reg #(Bool)) crg_rd_data_full <- mkCReg (3, False); - Reg #(AXI4_Rd_Data #(wd_id, wd_data, wd_user)) rg_rd_data <- mkRegU; - - // The following CReg port indexes specify the relative scheduling of: - // {first,deq,notEmpty} {enq,notFull} clear - Integer port_deq = 0; - Integer port_enq = 1; - Integer port_clear = 2; - - // ---------------------------------------------------------------- - // INTERFACE - - method Action reset; - crg_wr_addr_full [port_clear] <= False; - crg_wr_data_full [port_clear] <= False; - crg_wr_resp_full [port_clear] <= False; - crg_rd_addr_full [port_clear] <= False; - crg_rd_data_full [port_clear] <= False; - endmethod - - // AXI side - interface axi_side = interface AXI4_S_IFC; - // Wr Addr channel - method Action m_awvalid (Bool awvalid, - Bit #(wd_id) awid, - Bit #(wd_dest) awaddr, - Bit #(8) awlen, - AXI4_Size awsize, - Bit #(2) awburst, - Bit #(1) awlock, - Bit #(4) awcache, - Bit #(3) awprot, - Bit #(4) awqos, - Bit #(4) awregion, - Bit #(wd_user) awuser); - - if (awvalid && (! crg_wr_addr_full [port_enq])) begin - crg_wr_addr_full [port_enq] <= True; // enq - rg_wr_addr <= AXI4_Wr_Addr {awid: awid, - awaddr: awaddr, - awlen: awlen, - awsize: awsize, - awburst: awburst, - awlock: awlock, - awcache: awcache, - awprot: awprot, - awqos: awqos, - awregion: awregion, - awuser: awuser}; - end - endmethod - - method Bool m_awready; - return (! crg_wr_addr_full [port_enq]); - endmethod - - // Wr Data channel - method Action m_wvalid (Bool wvalid, - Bit #(wd_id) wid, - Bit #(wd_data) wdata, - Bit #(TDiv #(wd_data, 8)) wstrb, - Bool wlast, - Bit #(wd_user) wuser); - if (wvalid && (! crg_wr_data_full [port_enq])) begin - crg_wr_data_full [port_enq] <= True; // enq - rg_wr_data <= AXI4_Wr_Data {wid: wid, - wdata: wdata, - wstrb: wstrb, - wlast: wlast, - wuser: wuser}; - end - endmethod - - method Bool m_wready; - return (! crg_wr_data_full [port_enq]); - endmethod - - // Wr Response channel - method Bool m_bvalid = crg_wr_resp_full [port_deq]; - method Bit #(wd_id) m_bid = rg_wr_resp.bid; - method Bit #(2) m_bresp = rg_wr_resp.bresp; - method Bit #(wd_user) m_buser = rg_wr_resp.buser; - method Action m_bready (Bool bready); - if (bready && crg_wr_resp_full [port_deq]) - crg_wr_resp_full [port_deq] <= False; // deq - endmethod - - // Rd Addr channel - method Action m_arvalid (Bool arvalid, - Bit #(wd_id) arid, - Bit #(wd_dest) araddr, - Bit #(8) arlen, - AXI4_Size arsize, - Bit #(2) arburst, - Bit #(1) arlock, - Bit #(4) arcache, - Bit #(3) arprot, - Bit #(4) arqos, - Bit #(4) arregion, - Bit #(wd_user) aruser); - if (arvalid && (! crg_rd_addr_full [port_enq])) begin - crg_rd_addr_full [port_enq] <= True; // enq - rg_rd_addr <= AXI4_Rd_Addr {arid: arid, - araddr: araddr, - arlen: arlen, - arsize: arsize, - arburst: arburst, - arlock: arlock, - arcache: arcache, - arprot: arprot, - arqos: arqos, - arregion: arregion, - aruser: aruser}; - end - endmethod - - method Bool m_arready; - return (! crg_rd_addr_full [port_enq]); - endmethod - - // Rd Data channel - method Bool m_rvalid = crg_rd_data_full [port_deq]; - method Bit #(wd_id) m_rid = rg_rd_data.rid; - method Bit #(wd_data) m_rdata = rg_rd_data.rdata; - method Bit #(2) m_rresp = rg_rd_data.rresp; - method Bool m_rlast = rg_rd_data.rlast; - method Bit #(wd_user) m_ruser = rg_rd_data.ruser; - method Action m_rready (Bool rready); - if (rready && crg_rd_data_full [port_deq]) - crg_rd_data_full [port_deq] <= False; // deq - endmethod - endinterface; - - // FIFOF side - interface o_wr_addr = fn_crg_and_rg_to_FIFOF_O (crg_wr_addr_full [port_deq], rg_wr_addr); - interface o_wr_data = fn_crg_and_rg_to_FIFOF_O (crg_wr_data_full [port_deq], rg_wr_data); - interface i_wr_resp = fn_crg_and_rg_to_FIFOF_I (crg_wr_resp_full [port_enq], rg_wr_resp); - - interface o_rd_addr = fn_crg_and_rg_to_FIFOF_O (crg_rd_addr_full [port_deq], rg_rd_addr); - interface i_rd_data = fn_crg_and_rg_to_FIFOF_I (crg_rd_data_full [port_enq], rg_rd_data); -endmodule: mkAXI4_S_Xactor_2 -*/ // ================================================================ endpackage diff --git a/Libraries/AMBA_Fabrics/AXI4_Stream/README_AXI4S.adoc b/Libraries/AMBA_Fabrics/AXI4_Stream/README_AXI4S.adoc new file mode 100644 index 0000000..d8e61bb --- /dev/null +++ b/Libraries/AMBA_Fabrics/AXI4_Stream/README_AXI4S.adoc @@ -0,0 +1,58 @@ += About `bsc-contrib/Libraries/AMBA_Fabrics/AXI4_Stream` +:revnumber: v1.00 +:revdate: 2024-12-07 +:sectnums: +:imagesdir: ../Doc/Figs +:toc: +:toclevels: 3 +:toc-title: Contents +:keywords: Bluespec, B-Lang, BSV, BH, AMBA, ARM AXI, AXI4, AXI4-Lite, AXI4-Stream + +// ================================================================ + +Copyright (C) 2017-2023 Bluespec, Inc. All Rights Reserved + +Copyright (C) 2024-2025 B-Lang.org. All Rights Reserved + +SPDX-License-Identifier: BSD-3-Clause + +// ================================================================ + +Pleae see README in parent directory +(link:../README_AMBA_Fabrics.adoc[adoc],link:../README_AMBA_Fabrics.html[html]) +for general introduction and about compiling/building/testing. + +This directory describes AXI4-Stream facilities. + +These source codes may import other `bsv-contrib` libraries; be sure +they are visible in your _bsc_ compiler paths: + + bsc-contrib/Libraries/AMBA_Fabrics/Utils/ + bsc-contrib/Libraries/Misc/ + +// ================================================================ + +image::IMG_Under_Construction.png[align="left", width=100] + +The packages in this directory can be used as-is, but are expected to +be restructured to be more like their AXI4 siblings (more consistent +naming, clean separation of B-Lang-style and RTL-style, etc.) + +This documentation will be updated after that restructuring. + +// ================================================================ +== `AXI4_Stream.bsv` + +(... To be written ...) + +// ================================================================ +== Unit tests + +Unit tests will be found in: + + bsc-contrib/testing/bsc.contrib/AMBA_Fabrics/AXI4_Stream/Test_*.bsv + +The conventions for unit tests are described in more detail in the +README in the parent directory +(link:../README_AMBA_Fabrics.adoc[adoc],link:../README_AMBA_Fabrics.html[html]). + +// ================================================================ diff --git a/Libraries/AMBA_Fabrics/AXI4_Stream/README_AXI4S.html b/Libraries/AMBA_Fabrics/AXI4_Stream/README_AXI4S.html new file mode 100644 index 0000000..0350f0f --- /dev/null +++ b/Libraries/AMBA_Fabrics/AXI4_Stream/README_AXI4S.html @@ -0,0 +1,530 @@ + + + + + + + + +About bsc-contrib/Libraries/AMBA_Fabrics/AXI4_Stream + + + + + +
+
+
+
+

Copyright © 2017-2023 Bluespec, Inc. All Rights Reserved
+Copyright © 2024-2025 B-Lang.org. All Rights Reserved

+
+
+

SPDX-License-Identifier: BSD-3-Clause

+
+
+

Pleae see README in parent directory +(adoc,html) +for general introduction and about compiling/building/testing.

+
+
+

This directory describes AXI4-Stream facilities.

+
+
+

These source codes may import other bsv-contrib libraries; be sure +they are visible in your bsc compiler paths:

+
+
+
+
bsc-contrib/Libraries/AMBA_Fabrics/Utils/
+bsc-contrib/Libraries/Misc/
+
+
+
+
+IMG Under Construction +
+
+
+

The packages in this directory can be used as-is, but are expected to +be restructured to be more like their AXI4 siblings (more consistent +naming, clean separation of B-Lang-style and RTL-style, etc.)

+
+
+

This documentation will be updated after that restructuring.

+
+
+
+
+

1. AXI4_Stream.bsv

+
+
+

(…​ To be written …​)

+
+
+
+
+

2. Unit tests

+
+
+

Unit tests will be found in:

+
+
+
+
bsc-contrib/testing/bsc.contrib/AMBA_Fabrics/AXI4_Stream/Test_*.bsv
+
+
+
+

The conventions for unit tests are described in more detail in the +README in the parent directory +(adoc,html).

+
+
+
+
+ + + \ No newline at end of file diff --git a/Libraries/AMBA_Fabrics/Adapters/AXI4L_S_to_AXI4_M_Adapter.bsv b/Libraries/AMBA_Fabrics/Adapters/AXI4L_S_to_AXI4_M_Adapter.bsv index e13c275..4db5422 100644 --- a/Libraries/AMBA_Fabrics/Adapters/AXI4L_S_to_AXI4_M_Adapter.bsv +++ b/Libraries/AMBA_Fabrics/Adapters/AXI4L_S_to_AXI4_M_Adapter.bsv @@ -19,7 +19,7 @@ import FIFOF :: *; import SpecialFIFOs :: *; // ---------------- -// BSV additional libs +// Bluespec misc. libs import Cur_Cycle :: *; import GetPut_Aux :: *; @@ -27,9 +27,10 @@ import GetPut_Aux :: *; // ================================================================ // Project imports -import Semi_FIFOF :: *; -import AXI4L_Types :: *; -import AXI4_Types :: *; +import Semi_FIFOF :: *; +import AXI4L_Types :: *; +import AXI4_Types :: *; +import AXI4_BSV_RTL :: *; // ================================================================ // The interface for the adapter module @@ -56,10 +57,10 @@ interface AXI4L_S_to_AXI4_M_Adapter_IFC #(numeric type wd_addr_AXI4L_S, wd_user_AXI4L_S) ifc_AXI4L_S; // AXI4 M side - interface AXI4_M_IFC #(wd_id_AXI4_M, - wd_addr_AXI4_M, - wd_data_AXI4_M, - wd_user_AXI4_M) ifc_AXI4_M; + interface AXI4_RTL_M_IFC #(wd_id_AXI4_M, + wd_addr_AXI4_M, + wd_data_AXI4_M, + wd_user_AXI4_M) ifc_AXI4_M; endinterface // ================================================================ @@ -96,10 +97,10 @@ module mkAXI4L_S_to_AXI4_M_Adapter (AXI4L_S_to_AXI4_M_Adapter_IFC #(wd_addr_AXI4 wd_user_AXI4L_S) xactor_AXI4L_S <- mkAXI4L_S_Xactor; // AXI4 M transactor - AXI4_M_Xactor_IFC #(wd_id_AXI4_M, - wd_addr_AXI4_M, - wd_data_AXI4_M, - wd_user_AXI4_M) xactor_AXI4_M <- mkAXI4_M_Xactor; + AXI4_BSV_to_RTL_IFC #(wd_id_AXI4_M, + wd_addr_AXI4_M, + wd_data_AXI4_M, + wd_user_AXI4_M) xactor_AXI4_M <- mkAXI4_BSV_to_RTL; // ================================================================ // BEHAVIOR @@ -107,26 +108,26 @@ module mkAXI4L_S_to_AXI4_M_Adapter (AXI4L_S_to_AXI4_M_Adapter_IFC #(wd_addr_AXI4 // ---------------------------------------------------------------- // Write requests (Write Address and Write Data channels) - rule rl_wr_addr_data; - let awaddr_AXI4L <- pop_o (xactor_AXI4L_S.o_wr_addr); - let wdata_AXI4L <- pop_o (xactor_AXI4L_S.o_wr_data); + rule rl_AW_W; + let aw_AXI4L <- pop_o (xactor_AXI4L_S.o_wr_addr); + let w_AXI4L <- pop_o (xactor_AXI4L_S.o_wr_data); Bit #(wd_id_AXI4_M) id_AXI4 = 1; - Bit #(wd_addr_AXI4_M) addr_AXI4 = zeroExtend (awaddr_AXI4L.awaddr); - Bit #(wd_user_AXI4_M) user_AXI4 = zeroExtend (awaddr_AXI4L.awuser); - - AXI4_Wr_Addr #(wd_id_AXI4_M, wd_addr_AXI4_M, wd_user_AXI4_M) - awaddr_AXI4 = AXI4_Wr_Addr {awid: id_AXI4, - awaddr: addr_AXI4, - awlen: 0, // AXI4 encoding for "1 beat" - awsize: axsize_4, - awburst: axburst_fixed, - awlock: axlock_normal, - awcache: awcache_dev_nonbuf, - awprot: awaddr_AXI4L.awprot, - awqos: 0, - awregion: 0, - awuser: user_AXI4}; + Bit #(wd_addr_AXI4_M) addr_AXI4 = zeroExtend (aw_AXI4L.awaddr); + Bit #(wd_user_AXI4_M) user_AXI4 = zeroExtend (aw_AXI4L.awuser); + + AXI4_AW #(wd_id_AXI4_M, wd_addr_AXI4_M, wd_user_AXI4_M) + aw_AXI4 = AXI4_AW {awid: id_AXI4, + awaddr: addr_AXI4, + awlen: 0, // AXI4 encoding for "1 beat" + awsize: axsize_4, + awburst: axburst_fixed, + awlock: axlock_normal, + awcache: awcache_dev_nonbuf, + awprot: aw_AXI4L.awprot, + awqos: 0, + awregion: 0, + awuser: user_AXI4}; // Lane-align wdata for the wider AXI4 @@ -135,47 +136,47 @@ module mkAXI4L_S_to_AXI4_M_Adapter (AXI4L_S_to_AXI4_M_Adapter_IFC #(wd_addr_AXI4 Integer hi = valueOf (addr_index_M_t) - 1; Integer lo = valueOf (addr_index_S_t); - Bit #(index_width_t) index = awaddr_AXI4L.awaddr [hi:lo]; - v_data [index] = wdata_AXI4L.wdata; - v_strb [index] = wdata_AXI4L.wstrb; + Bit #(index_width_t) index = aw_AXI4L.awaddr [hi:lo]; + v_data [index] = w_AXI4L.wdata; + v_strb [index] = w_AXI4L.wstrb; - AXI4_Wr_Data #(wd_data_AXI4_M, wd_user_AXI4_M) - wdata_AXI4 = AXI4_Wr_Data {wdata: pack (v_data), - wstrb: pack (v_strb), - wlast: True, - wuser: user_AXI4}; + AXI4_W #(wd_data_AXI4_M, wd_user_AXI4_M) + w_AXI4 = AXI4_W {wdata: pack (v_data), + wstrb: pack (v_strb), + wlast: True, + wuser: user_AXI4}; - xactor_AXI4_M.i_wr_addr.enq (awaddr_AXI4); - xactor_AXI4_M.i_wr_data.enq (wdata_AXI4); + xactor_AXI4_M.ifc_S.i_AW.enq (aw_AXI4); + xactor_AXI4_M.ifc_S.i_W.enq (w_AXI4); // Debugging if (verbosity > 0) begin - $display ("%0d: %m.rl_wr_addr_data:", cur_cycle); - $display (" ", fshow (awaddr_AXI4L)); - $display (" ==> ", fshow (awaddr_AXI4)); - $display (" ", fshow (wdata_AXI4)); - $display (" ==> ", fshow (wdata_AXI4)); + $display ("%0d: rl_AW_W:", cur_cycle); + $display (" ", fshow (aw_AXI4L)); + $display (" ==> ", fshow (aw_AXI4)); + $display (" ", fshow (w_AXI4)); + $display (" ==> ", fshow (w_AXI4)); end endrule // ---------------------------------------------------------------- - // Write responses (Write Data channel) + // B channel (write responses) - rule rl_wr_resp; - let wr_resp_AXI4 <- pop_o (xactor_AXI4_M.o_wr_resp); + rule rl_B; + let b_AXI4 <- pop_o (xactor_AXI4_M.ifc_S.o_B); - Bit #(wd_user_AXI4L_S) user = truncate (wr_resp_AXI4.buser); + Bit #(wd_user_AXI4L_S) user = truncate (b_AXI4.buser); AXI4L_Wr_Resp #(wd_user_AXI4L_S) - wr_resp_AXI4L = AXI4L_Wr_Resp {bresp: unpack (wr_resp_AXI4.bresp), - buser: user}; - xactor_AXI4L_S.i_wr_resp.enq (wr_resp_AXI4L); + b_AXI4L = AXI4L_Wr_Resp {bresp: unpack (b_AXI4.bresp), + buser: user}; + xactor_AXI4L_S.i_wr_resp.enq (b_AXI4L); // Debugging if (verbosity > 0) begin - $display ("%0d: %m.rl_wr_resp:", cur_cycle); - $display (" ", fshow (wr_resp_AXI4)); - $display (" ==> ", fshow (wr_resp_AXI4L)); + $display ("%0d: rl_B:", cur_cycle); + $display (" ", fshow (b_AXI4)); + $display (" ==> ", fshow (b_AXI4L)); end endrule @@ -188,69 +189,69 @@ module mkAXI4L_S_to_AXI4_M_Adapter (AXI4L_S_to_AXI4_M_Adapter_IFC #(wd_addr_AXI4 // ---------------- - rule rl_rd_addr; - let araddr_AXI4L <- pop_o (xactor_AXI4L_S.o_rd_addr); + rule rl_AR; + let ar_AXI4L <- pop_o (xactor_AXI4L_S.o_rd_addr); Bit #(wd_id_AXI4_M) id_AXI4 = 1; - Bit #(wd_addr_AXI4_M) addr_AXI4 = zeroExtend (araddr_AXI4L.araddr); - Bit #(wd_user_AXI4_M) user_AXI4 = zeroExtend (araddr_AXI4L.aruser); - - AXI4_Rd_Addr #(wd_id_AXI4_M, wd_addr_AXI4_M, wd_user_AXI4_M) - araddr_AXI4 = AXI4_Rd_Addr {arid: id_AXI4, - araddr: addr_AXI4, - arlen: 0, // AXI4 encoding for "1 beat" - arsize: axsize_4, - arburst: axburst_fixed, - arlock: axlock_normal, - arcache: arcache_dev_nonbuf, - arprot: araddr_AXI4L.arprot, - arqos: 0, - arregion: 0, - aruser: user_AXI4}; - - xactor_AXI4_M.i_rd_addr.enq (araddr_AXI4); + Bit #(wd_addr_AXI4_M) addr_AXI4 = zeroExtend (ar_AXI4L.araddr); + Bit #(wd_user_AXI4_M) user_AXI4 = zeroExtend (ar_AXI4L.aruser); + + AXI4_AR #(wd_id_AXI4_M, wd_addr_AXI4_M, wd_user_AXI4_M) + ar_AXI4 = AXI4_AR {arid: id_AXI4, + araddr: addr_AXI4, + arlen: 0, // AXI4 encoding for "1 beat" + arsize: axsize_4, + arburst: axburst_fixed, + arlock: axlock_normal, + arcache: arcache_dev_nonbuf, + arprot: ar_AXI4L.arprot, + arqos: 0, + arregion: 0, + aruser: user_AXI4}; + + xactor_AXI4_M.ifc_S.i_AR.enq (ar_AXI4); // Remember addrs so returned data can properly lane-aligned - f_rd_addrs.enq (araddr_AXI4L.araddr); + f_rd_addrs.enq (ar_AXI4L.araddr); // Debugging if (verbosity > 0) begin - $display ("%0d: %m.rl_rd_addr:", cur_cycle); - $display (" ", fshow (araddr_AXI4L)); - $display (" ==> ", fshow (araddr_AXI4)); + $display ("%0d: rl_AR:", cur_cycle); + $display (" ", fshow (ar_AXI4L)); + $display (" ==> ", fshow (ar_AXI4)); end endrule // ---------------------------------------------------------------- // Read responses (Read Data channel) - rule rl_rd_data; - let rd_data_AXI4 <- pop_o (xactor_AXI4_M.o_rd_data); + rule rl_R; + let r_AXI4 <- pop_o (xactor_AXI4_M.ifc_S.o_R); let addr_AXI4L <- pop (f_rd_addrs); // Lane-align rdata for the narrower AXI4L - Vector #(expansion_t, Bit #(wd_data_AXI4L_S)) v_data = unpack (rd_data_AXI4.rdata); + Vector #(expansion_t, Bit #(wd_data_AXI4L_S)) v_data = unpack (r_AXI4.rdata); Integer hi = valueOf (addr_index_M_t) - 1; Integer lo = valueOf (addr_index_S_t); Bit #(index_width_t) index = addr_AXI4L [hi:lo]; Bit #(wd_data_AXI4L_S) rdata_AXI4L = v_data [index]; - Bit #(wd_user_AXI4L_S) ruser_AXI4L = truncate (rd_data_AXI4.ruser); + Bit #(wd_user_AXI4L_S) ruser_AXI4L = truncate (r_AXI4.ruser); AXI4L_Rd_Data #(wd_data_AXI4L_S, wd_user_AXI4L_S) - rd_data_AXI4L = AXI4L_Rd_Data {rresp: unpack (rd_data_AXI4.rresp), - rdata: rdata_AXI4L, - ruser: ruser_AXI4L }; + r_AXI4L = AXI4L_Rd_Data {rresp: unpack (r_AXI4.rresp), + rdata: rdata_AXI4L, + ruser: ruser_AXI4L }; - xactor_AXI4L_S.i_rd_data.enq (rd_data_AXI4L); + xactor_AXI4L_S.i_rd_data.enq (r_AXI4L); // Debugging if (verbosity > 0) begin - $display ("%0d: %m.rl_rd_data:", cur_cycle); - $display (" ", fshow (rd_data_AXI4)); - $display (" ==> ", fshow (rd_data_AXI4L)); + $display ("%0d: rl_R:", cur_cycle); + $display (" ", fshow (r_AXI4)); + $display (" ==> ", fshow (r_AXI4L)); end endrule @@ -258,7 +259,7 @@ module mkAXI4L_S_to_AXI4_M_Adapter (AXI4L_S_to_AXI4_M_Adapter_IFC #(wd_addr_AXI4 // INTERFACE interface ifc_AXI4L_S = xactor_AXI4L_S.axi_side; - interface ifc_AXI4_M = xactor_AXI4_M .axi_side; + interface ifc_AXI4_M = xactor_AXI4_M .rtl_M; endmodule // ================================================================ diff --git a/Libraries/AMBA_Fabrics/Adapters/AXI4_AXI4L_Adapters.bsv b/Libraries/AMBA_Fabrics/Adapters/AXI4_AXI4L_Adapters.bsv index 213a4cd..8c28714 100644 --- a/Libraries/AMBA_Fabrics/Adapters/AXI4_AXI4L_Adapters.bsv +++ b/Libraries/AMBA_Fabrics/Adapters/AXI4_AXI4L_Adapters.bsv @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Bluespec, Inc. All Rights Reserved +// Copyright (c) 2019-2024 Bluespec, Inc. All Rights Reserved // // SPDX-License-Identifier: BSD-3-Clause @@ -21,7 +21,7 @@ package AXI4_AXI4L_Adapters; export -fn_AXI4L_M_IFC_to_AXI4_M_IFC; +fn_AXI4L_M_IFC_to_AXI4_RTL_M_IFC; // ================================================================ // BSV library imports @@ -30,7 +30,7 @@ import FIFOF :: *; import Connectable :: *; // ---------------- -// BSV additional libs +// Bluespec misc. libs import Semi_FIFOF :: *; import EdgeFIFOFs :: *; @@ -38,8 +38,9 @@ import EdgeFIFOFs :: *; // ================================================================ // Project imports -import AXI4L_Types :: *; -import AXI4_Types :: *; +import AXI4L_Types :: *; +import AXI4_Types :: *; +import AXI4_BSV_RTL :: *; // ================================================================ // Compute the encoding of AWSIZE/ARSIZE @@ -55,12 +56,12 @@ endfunction // ================================================================ -function AXI4_M_IFC #(wd_id, wd_addr, wd_data, wd_user) - fn_AXI4L_M_IFC_to_AXI4_M_IFC +function AXI4_RTL_M_IFC #(wd_id, wd_addr, wd_data, wd_user) + fn_AXI4L_M_IFC_to_AXI4_RTL_M_IFC (AXI4L_M_IFC #(wd_addr, wd_data, wd_user) axi4L); return - interface AXI4_M_IFC; + interface AXI4_RTL_M_IFC; // ---------------- // Wr Addr channel @@ -151,8 +152,8 @@ endfunction // Transformer to get AXI4L S interface from an AXI4 S interface function AXI4L_S_IFC #(wd_addr, wd_data, wd_user) - fv_AXI4_S_IFC_to_AXI4L_S_IFC - (AXI4_S_IFC #(wd_id, wd_addr, wd_data, wd_user) axi4); + fv_AXI4_RTL_S_IFC_to_AXI4L_S_IFC + (AXI4_RTL_S_IFC #(wd_id, wd_addr, wd_data, wd_user) axi4); return interface AXI4L_S_IFC; diff --git a/Libraries/AMBA_Fabrics/Adapters/README_AXI_Adapters.adoc b/Libraries/AMBA_Fabrics/Adapters/README_AXI_Adapters.adoc new file mode 100644 index 0000000..b4c3ac8 --- /dev/null +++ b/Libraries/AMBA_Fabrics/Adapters/README_AXI_Adapters.adoc @@ -0,0 +1,71 @@ += About `bsc-contrib/Libraries/AMBA_Fabrics/Adapters` +:revnumber: v1.00 +:revdate: 2024-12-07 +:sectnums: +:imagesdir: ../Doc/Figs +:toc: +:toclevels: 3 +:toc-title: Contents +:keywords: Bluespec, B-Lang, BSV, BH, AMBA, ARM AXI, AXI4, AXI4-Lite, AXI4-Stream + +// ================================================================ + +Copyright (C) 2017-2023 Bluespec, Inc. All Rights Reserved + +Copyright (C) 2024-2025 B-Lang.org. All Rights Reserved + +SPDX-License-Identifier: BSD-3-Clause + +// ================================================================ + +Pleae see README in parent directory +(link:../README_AMBA_Fabrics.adoc[adoc],link:../README_AMBA_Fabrics.html[html]) +for general introduction and about compiling/building/testing. + +This directory describes adapters between AXI4 and AXI4-Lite. + +These source codes may import other `bsv-contrib` libraries; be sure +they are visible in your _bsc_ compiler paths: + + bsc-contrib/Libraries/AMBA_Fabrics/Utils/ + bsc-contrib/Libraries/Misc/ + +These source codes may import other `bsv-contrib` libraries; be sure +they are visible in your _bsc_ compiler paths: + + bsc-contrib/Libraries/AMBA_Fabrics/Utils/ + bsc-contrib/Libraries/Misc/ + +// ================================================================ + +image::IMG_Under_Construction.png[align="left", width=100] + +The packages in this directory can be used as-is, but are expected to +be restructured to be more like their AXI4 siblings (more consistent +naming, clean separation of B-Lang-style and RTL-style, etc.) + +This documentation will be updated after that restructuring. + +// ================================================================ +== `AXI4L_S_to_AXI4_M_Adapter.bsv` + +A module to bridge from an AXI4-Lite S to an AXI4 M. + +(... To be written ...) + +// ================================================================ +== `AXI4_AXI4_Lite_Adapters.bsv` + +(... To be written ...) + +// ================================================================ +== Unit tests + +Unit tests will be found in: + + bsc-contrib/testing/bsc.contrib/AMBA_Fabrics/Adapters/Test_*.bsv + +The conventions for unit tests are described in more detail in the +README in the parent directory +(link:../README_AMBA_Fabrics.adoc[adoc],link:../README_AMBA_Fabrics.html[html]). + +// ================================================================ diff --git a/Libraries/AMBA_Fabrics/Adapters/README_AXI_Adapters.html b/Libraries/AMBA_Fabrics/Adapters/README_AXI_Adapters.html new file mode 100644 index 0000000..dfbb502 --- /dev/null +++ b/Libraries/AMBA_Fabrics/Adapters/README_AXI_Adapters.html @@ -0,0 +1,552 @@ + + + + + + + + +About bsc-contrib/Libraries/AMBA_Fabrics/Adapters + + + + + +
+
+
+
+

Copyright © 2017-2023 Bluespec, Inc. All Rights Reserved
+Copyright © 2024-2025 B-Lang.org. All Rights Reserved

+
+
+

SPDX-License-Identifier: BSD-3-Clause

+
+
+

Pleae see README in parent directory +(adoc,html) +for general introduction and about compiling/building/testing.

+
+
+

This directory describes adapters between AXI4 and AXI4-Lite.

+
+
+

These source codes may import other bsv-contrib libraries; be sure +they are visible in your bsc compiler paths:

+
+
+
+
bsc-contrib/Libraries/AMBA_Fabrics/Utils/
+bsc-contrib/Libraries/Misc/
+
+
+
+

These source codes may import other bsv-contrib libraries; be sure +they are visible in your bsc compiler paths:

+
+
+
+
bsc-contrib/Libraries/AMBA_Fabrics/Utils/
+bsc-contrib/Libraries/Misc/
+
+
+
+
+IMG Under Construction +
+
+
+

The packages in this directory can be used as-is, but are expected to +be restructured to be more like their AXI4 siblings (more consistent +naming, clean separation of B-Lang-style and RTL-style, etc.)

+
+
+

This documentation will be updated after that restructuring.

+
+
+
+
+

1. AXI4L_S_to_AXI4_M_Adapter.bsv

+
+
+

A module to bridge from an AXI4-Lite S to an AXI4 M.

+
+
+

(…​ To be written …​)

+
+
+
+
+

2. AXI4_AXI4_Lite_Adapters.bsv

+
+
+

(…​ To be written …​)

+
+
+
+
+

3. Unit tests

+
+
+

Unit tests will be found in:

+
+
+
+
bsc-contrib/testing/bsc.contrib/AMBA_Fabrics/Adapters/Test_*.bsv
+
+
+
+

The conventions for unit tests are described in more detail in the +README in the parent directory +(adoc,html).

+
+
+
+
+ + + \ No newline at end of file diff --git a/Libraries/AMBA_Fabrics/Doc/Figs/IMG_Under_Construction.png b/Libraries/AMBA_Fabrics/Doc/Figs/IMG_Under_Construction.png new file mode 100644 index 0000000..9b4a346 Binary files /dev/null and b/Libraries/AMBA_Fabrics/Doc/Figs/IMG_Under_Construction.png differ diff --git a/Libraries/AMBA_Fabrics/Doc/Figs/RSN_2024-12-07.000.00.png b/Libraries/AMBA_Fabrics/Doc/Figs/RSN_2024-12-07.000.00.png new file mode 100644 index 0000000..eb5785e Binary files /dev/null and b/Libraries/AMBA_Fabrics/Doc/Figs/RSN_2024-12-07.000.00.png differ diff --git a/Libraries/AMBA_Fabrics/Makefile b/Libraries/AMBA_Fabrics/Makefile index 3cd8328..76e66a7 100644 --- a/Libraries/AMBA_Fabrics/Makefile +++ b/Libraries/AMBA_Fabrics/Makefile @@ -1,11 +1,10 @@ - # Enumerate all sub-dirs for recursive traversal BUILD_ORDER = \ + Utils \ AXI4 \ AXI4_Lite \ AXI4_Stream \ Adapters \ - Utils \ .PHONY: all all: install @@ -13,4 +12,3 @@ all: install .PHONY: install clean full_clean install clean full_clean: $(foreach dir, $(BUILD_ORDER), $(MAKE) -C $(dir) $@ &&) true - diff --git a/Libraries/AMBA_Fabrics/README_AMBA_Fabrics.adoc b/Libraries/AMBA_Fabrics/README_AMBA_Fabrics.adoc new file mode 100644 index 0000000..fad447e --- /dev/null +++ b/Libraries/AMBA_Fabrics/README_AMBA_Fabrics.adoc @@ -0,0 +1,191 @@ += About `bsc-contrib/Libraries/AMBA_Fabrics` +:revnumber: v1.00 +:revdate: 2024-12-07 +:sectnums: +:imagesdir: Doc/Figs +:toc: +:toclevels: 3 +:toc-title: Contents +:keywords: Bluespec, B-Lang, BSV, BH, AMBA, ARM AXI, AXI4, AXI4-Lite, AXI4-Stream + +// ================================================================ + +Copyright (C) 2017-2023 Bluespec, Inc. All Rights Reserved + +Copyright (C) 2024-2025 B-Lang.org. All Rights Reserved + +SPDX-License-Identifier: BSD-3-Clause + +NOTE: AMBA, ARM AXI, AXI4, AXI4-Lite, AXI4-Stream are specifications +by the company _ARM Limited_. The specs are open (no license required) +and can be downloaded from +https://www.arm.com/architecture/system-architectures/amba/amba-specifications[ARM's website]. + +// ================================================================ +== Introduction + +This directory contains libraries for various AMBA resources (AXI4, +AXI4-Lite, AXI4_Stream) including type definitions, cross-bar +switches, connectors, clock-crossers, and edge-transactors to connect +the B-Lang world to existing RTL (e.g., external AMBA IP). + +The source code is written in a B-Lang (BSV/BH/Bluespec Classic). The +free and open-source _bsc_ compiler tool can be used to generate +synthesizable Verilog (https://github.com/B-Lang-org/bsc[]). + +The origins of this code date at least as far back as 2018 inside +https://bluespec.com[Bluespec, Inc.] The codes have evolved over time +and various versions have been used and are still in use in numerous +RISC-V CPUs and SoC designs at several universities and companies, +including in commercialized ASICs. The codes were moved to this +repository in November-December 2024. + +CAUTION: In moving these codes to this repository, they are being + further cleaned up, so there may be a short period of + instability. The cleanups to the `AXI4/` directory have been + completed; cleanups to `AXI4_Lite` and `AXI4_Stream` + directories are ongoing. We will remove this CAUTION when + that work is completed. + +// ---------------------------------------------------------------- +=== Terminology: + +Since 2021 ARM has replaced the terms "Master" and "Slave" by +"Manager" and "Subordinate", respectively; in this library we just use +"M" and "S" for these concepts. An M sends requests (on AW, W, AR) +and recieves responses (on B, R). An S recieves requests (on AW, W, +AR) and sends responses (on B, R). + +In this library, identifiers use `AXI4` for AXI4 itsef, `AXI4L` for +AXI4-Lite, and `AXI4S` for AXI4-Stream, three distinct parts of the +AMBA AXI spec. + +// ================================================================ +== Principles + +=== Background on ARM AMBA AXI + +AXI4 and AXI4-Lite are specifications of buses, each with five +independent "channels" (AW, W, B, AR, R). + +ARM's specs are described at the signal (RTL) level). Each channel +has multiple sub-buses (e.g., awaddr, awsize, arlen, bresp, rdata, +...) and is unidirectional (all sub-buses carry data in the same +direction). Each channel is independently flow-controlled with a +traditional handshake on two additional wires called `ready` (receiver +to sender) and `valid` (sender-to-receiver). Please see "RTL view" in +diagram below. + +For a write-transaction, the sender sends information on AW (address +etc.) and W (data etc.), and receives a response on B (status etc.). +For a read-transaction, the sender sends information on AR (address +etc.) and receives a response on R (status, data, etc.). + +Please see ARM's specs for full details of the channels, sub-buses, +ready-valid handshaking, and detailed semantics of transactions. + +=== B-Lang view vs. RTL view + +In BSV/BH/Bluespec Classic the natural way to do unidirectional +flow-controlled communication is with FIFOs. Further, groups of +related data values are naturally represented as "structs". Further, +complex interfaces can contain sub-interfaces. + +Thus, AMBA AXI is represented in BSV as shown in "B-Lang view" in the +diagram below. Each of the groups AW, W, B, AR, R is defined as a +`struct` with fields corresponding to the sub-buses of the group. +Each struct is communicated as a unit from sender to receiver with +FIFO buffers. An M interface (`AXI4_M_IFC`) contains five +sub-interfaces, each corresponding to one end of a FIFO ("enqueue" end +or "dequeue" end). An S interface (`AXI4_S_IFC`) is the dual, using +the opposite ends of FIFOs. A one-line `mkConnection` is sufficient +to connect two such interfaces together. + +image::RSN_2024-12-07.000.00.png[align="left"] + +As long as one is working within a B-Lang, that is the complete story +on bus representation. However, if it is necessary to connect to +RTL-level AXI IP---Verilog/SystemVerilog buses, sub-buses, and +ready-valid handshaking--- we provide two "transactors" +`mkAXI4_BSV_to_RTL` (BSV M to RTL S) and `mkAXI4_RTL_to_BSV` (RTL M to +BSV S) to perform the conversions. Each transactor has a B-Lang-view +interface on one side and an RTL-view interface on the other side. +These transactors are written in BSV, but when compiled by the _bsc_ +compiler, will produced RTL signals and logic with the correct signal +names and ready-valid handshaking. Within B-Lang, a one-line +`mkConnection` is sufficient to connect `AXI4_RTL_M_IFC` and +`AXI4_RTL_S_IFC` interfaces. + +CAUTION: In these AXI4 resources, WRAP bursts are not supported. + Where bursts are supported, it generally assumes INCR + (incrementing) bursts or FIXED. + +// ================================================================ +== Directory structure and links to details + +The source tree for this library is: + + bsc-contrib_RSN/ + └── Libraries/ + └── AMBA_Fabrics/ + ├── README_AMBA_Fabrics.txt + ├── AXI4/ + ├── AXI4_Lite/ + ├── AXI4_Stream/ + ├── Adapters/ + ├── Utils/ + └── Makefile + +The code in `AXI4` is a substantially cleaned up version of existing +codes that have been used in a number of projects. The cleanup +involved consistent naming, clean separation of B-Lang-view and +RTL-view, externalizing of buffering choices, etc. + +The code in `AXI4_Lite`, `AXI4_Stream`, and `Adapters` are also +snapshots of existing codes that have been used in a number of +projects (and so they can be used, as-is), but they have not yet gone +through the analogous cleanup, which is a work-in-progress. + +Please see READMEs in each directory for specifics of directory +contents: + +* AXI4 (link:AXI4/README_AXI4.adoc[adoc], link:AXI4/README_AXI4.html[html]), +* AXI4_Lite (link:AXI4_Lite/README_AXI4L.adoc[adoc], link:AXI4_Lite/README_AXI4L.html[html]), +* AXI4_Stream (link:AXI4_Stream/README_AXI4S.adoc[adoc], link:AXI4_Stream/README_AXI4S.html[html]), +* Adapters (link:Adapters/README_AXI_Adapters.adoc[adoc], link:Adapters/README_AXI_Adapters.html[html]). + +// ================================================================ +== Compiling/Building/Testing + +You can compile these libraries from source using the _bsc_ compiler +as part of your own compile/build flow, by making sure the files you +need are in located in your _bsc_ compiler search path. + +NOTE: Some of these files require the `-aggressive-conditions` flag (else may deadlock). + +The Makefiles in this library can be used if you wish to pre-compile +these files using the _bsc_ compiler, by doing: + + $ make install + +This will invoke _bsc_ and place the resulting `.bo` files in: + + bsc-contrib/inst/lib/Libraries/AMBA_Fabrics/... + +There are several "unit tests" for this library located in: + + bsc-contrib_RSN/ + └── testing/ + └── bsc.contrib/ + ├── AMBA_Fabrics_AXI4/ + ├── AMBA_Fabrics_AXI4_Lite/ + ... + +Each directory, `Makefile`, script fragment (`foo.exp` file) and +expected output (`foo.out.expected` file) is set up for the CI +(Continuous Integration) flow that is run repeatedly for the _bsc_ +compiler. Please see the "How to Contribute to bsc-contrib" document +(link:../../doc/How_to_Contribute.adoc[adoc], +link:../../doc/How_to_Contribute.html[html]) for details on this +setup. + +// ================================================================ diff --git a/Libraries/AMBA_Fabrics/README_AMBA_Fabrics.html b/Libraries/AMBA_Fabrics/README_AMBA_Fabrics.html new file mode 100644 index 0000000..0d05d94 --- /dev/null +++ b/Libraries/AMBA_Fabrics/README_AMBA_Fabrics.html @@ -0,0 +1,754 @@ + + + + + + + + +About bsc-contrib/Libraries/AMBA_Fabrics + + + + + +
+
+
+
+

Copyright © 2017-2023 Bluespec, Inc. All Rights Reserved
+Copyright © 2024-2025 B-Lang.org. All Rights Reserved

+
+
+

SPDX-License-Identifier: BSD-3-Clause

+
+
+ + + + + +
+
Note
+
+AMBA, ARM AXI, AXI4, AXI4-Lite, AXI4-Stream are specifications +by the company ARM Limited. The specs are open (no license required) +and can be downloaded from +ARM’s website. +
+
+
+
+
+

1. Introduction

+
+
+

This directory contains libraries for various AMBA resources (AXI4, +AXI4-Lite, AXI4_Stream) including type definitions, cross-bar +switches, connectors, clock-crossers, and edge-transactors to connect +the B-Lang world to existing RTL (e.g., external AMBA IP).

+
+
+

The source code is written in a B-Lang (BSV/BH/Bluespec Classic). The +free and open-source bsc compiler tool can be used to generate +synthesizable Verilog (https://github.com/B-Lang-org/bsc).

+
+
+

The origins of this code date at least as far back as 2018 inside +Bluespec, Inc. The codes have evolved over time +and various versions have been used and are still in use in numerous +RISC-V CPUs and SoC designs at several universities and companies, +including in commercialized ASICs. The codes were moved to this +repository in November-December 2024.

+
+
+ + + + + +
+
Caution
+
+In moving these codes to this repository, they are being + further cleaned up, so there may be a short period of + instability. The cleanups to the AXI4/ directory have been + completed; cleanups to AXI4_Lite and AXI4_Stream + directories are ongoing. We will remove this CAUTION when + that work is completed. +
+
+
+

1.1. Terminology:

+
+

Since 2021 ARM has replaced the terms "Master" and "Slave" by +"Manager" and "Subordinate", respectively; in this library we just use +"M" and "S" for these concepts. An M sends requests (on AW, W, AR) +and recieves responses (on B, R). An S recieves requests (on AW, W, +AR) and sends responses (on B, R).

+
+
+

In this library, identifiers use AXI4 for AXI4 itsef, AXI4L for +AXI4-Lite, and AXI4S for AXI4-Stream, three distinct parts of the +AMBA AXI spec.

+
+
+
+
+
+

2. Principles

+
+
+

2.1. Background on ARM AMBA AXI

+
+

AXI4 and AXI4-Lite are specifications of buses, each with five +independent "channels" (AW, W, B, AR, R).

+
+
+

ARM’s specs are described at the signal (RTL) level). Each channel +has multiple sub-buses (e.g., awaddr, awsize, arlen, bresp, rdata, +…​) and is unidirectional (all sub-buses carry data in the same +direction). Each channel is independently flow-controlled with a +traditional handshake on two additional wires called ready (receiver +to sender) and valid (sender-to-receiver). Please see "RTL view" in +diagram below.

+
+
+

For a write-transaction, the sender sends information on AW (address +etc.) and W (data etc.), and receives a response on B (status etc.). +For a read-transaction, the sender sends information on AR (address +etc.) and receives a response on R (status, data, etc.).

+
+
+

Please see ARM’s specs for full details of the channels, sub-buses, +ready-valid handshaking, and detailed semantics of transactions.

+
+
+
+

2.2. B-Lang view vs. RTL view

+
+

In BSV/BH/Bluespec Classic the natural way to do unidirectional +flow-controlled communication is with FIFOs. Further, groups of +related data values are naturally represented as "structs". Further, +complex interfaces can contain sub-interfaces.

+
+
+

Thus, AMBA AXI is represented in BSV as shown in "B-Lang view" in the +diagram below. Each of the groups AW, W, B, AR, R is defined as a +struct with fields corresponding to the sub-buses of the group. +Each struct is communicated as a unit from sender to receiver with +FIFO buffers. An M interface (AXI4_M_IFC) contains five +sub-interfaces, each corresponding to one end of a FIFO ("enqueue" end +or "dequeue" end). An S interface (AXI4_S_IFC) is the dual, using +the opposite ends of FIFOs. A one-line mkConnection is sufficient +to connect two such interfaces together.

+
+
+
+RSN 2024 12 07.000.00 +
+
+
+

As long as one is working within a B-Lang, that is the complete story +on bus representation. However, if it is necessary to connect to +RTL-level AXI IP---Verilog/SystemVerilog buses, sub-buses, and +ready-valid handshaking--- we provide two "transactors" +mkAXI4_BSV_to_RTL (BSV M to RTL S) and mkAXI4_RTL_to_BSV (RTL M to +BSV S) to perform the conversions. Each transactor has a B-Lang-view +interface on one side and an RTL-view interface on the other side. +These transactors are written in BSV, but when compiled by the bsc +compiler, will produced RTL signals and logic with the correct signal +names and ready-valid handshaking. Within B-Lang, a one-line +mkConnection is sufficient to connect AXI4_RTL_M_IFC and +AXI4_RTL_S_IFC interfaces.

+
+
+ + + + + +
+
Caution
+
+In these AXI4 resources, WRAP bursts are not supported. + Where bursts are supported, it generally assumes INCR + (incrementing) bursts or FIXED. +
+
+
+
+
+
+ +
+
+

The source tree for this library is:

+
+
+
+
bsc-contrib_RSN/
+└── Libraries/
+    └── AMBA_Fabrics/
+        ├── README_AMBA_Fabrics.txt
+        ├── AXI4/
+        ├── AXI4_Lite/
+        ├── AXI4_Stream/
+        ├── Adapters/
+        ├── Utils/
+        └── Makefile
+
+
+
+

The code in AXI4 is a substantially cleaned up version of existing +codes that have been used in a number of projects. The cleanup +involved consistent naming, clean separation of B-Lang-view and +RTL-view, externalizing of buffering choices, etc.

+
+
+

The code in AXI4_Lite, AXI4_Stream, and Adapters are also +snapshots of existing codes that have been used in a number of +projects (and so they can be used, as-is), but they have not yet gone +through the analogous cleanup, which is a work-in-progress.

+
+
+

Please see READMEs in each directory for specifics of directory +contents:

+
+
+ +
+
+
+
+

4. Compiling/Building/Testing

+
+
+

You can compile these libraries from source using the bsc compiler +as part of your own compile/build flow, by making sure the files you +need are in located in your bsc compiler search path.

+
+
+ + + + + +
+
Note
+
+Some of these files require the -aggressive-conditions flag (else may deadlock). +
+
+
+

The Makefiles in this library can be used if you wish to pre-compile +these files using the bsc compiler, by doing:

+
+
+
+
$ make install
+
+
+
+

This will invoke bsc and place the resulting .bo files in:

+
+
+
+
bsc-contrib/inst/lib/Libraries/AMBA_Fabrics/...
+
+
+
+

There are several "unit tests" for this library located in:

+
+
+
+
bsc-contrib_RSN/
+└── testing/
+    └── bsc.contrib/
+        ├── AMBA_Fabrics_AXI4/
+        ├── AMBA_Fabrics_AXI4_Lite/
+        ...
+
+
+
+

Each directory, Makefile, script fragment (foo.exp file) and +expected output (foo.out.expected file) is set up for the CI +(Continuous Integration) flow that is run repeatedly for the bsc +compiler. Please see the "How to Contribute to bsc-contrib" document +(adoc, +html) for details on this +setup.

+
+
+
+
+ + + \ No newline at end of file diff --git a/Libraries/AMBA_Fabrics/README_AMBA_Fabrics.txt b/Libraries/AMBA_Fabrics/README_AMBA_Fabrics.txt deleted file mode 100644 index 0c7050f..0000000 --- a/Libraries/AMBA_Fabrics/README_AMBA_Fabrics.txt +++ /dev/null @@ -1,174 +0,0 @@ -Copyright (c) 2017-2024 Bluespec, Inc. All Rights Reserved - -SPDX-License-Identifier: BSD-3-Clause - -This directory contains B-Lang libraries for various AMBA resources -(AXI4, AXI4-Lite, AXI4_Stream) including type definitions, cross-bar -switches, connectors, clock-crossers, transactors to convert from -BSV-internal connection idioms (FIFOs) to AMBA connection signalling, -etc. - -The source code is written in Bluespec BSV. The `bsc` compiler tool -can be used to generate synthesizable Verilog. - -Some of these components have been used in numerous RISC-V CPUs and -SoC designs at Bluespec, Inc. - -CAUTION: When using resources from this library, please examine the - source code and check that it is fit for your purpose. - - The components here were added piecemeal, in an _ad hoc_ way, - over a number of years, each component serving some specific - immediate, narrow need. The whole library could do with a - ground-up rewrite, including filling in many obvious missing - components. - -CAUTION: In these resources, where bursts are supported, it generally - assumes INCR (incrementing) bursts, not FIXED or WRAP. - -// ================================================================ -Terminology: - -Since 2021 ARM has replaced the terms "Master" and "Slave" by -"Manager" and "Subordinate", respectively; in this library we adopt -this convention. Identifiers and file names use the abbreviated `M` -and `S`. - -In this library, identifiers use `AXI4` for AXI4 itsef and `AXI4L` for -AXI4-Lite. - -// ================================================================ -The source tree for this library is: - - bsc-contrib_RSN/ - ├── Libraries - │ ├── AMBA_Fabrics - │ │ ├── AXI4 - │ │ ├── AXI4_Lite - │ │ ├── AXI4_Stream - │ │ ├── Adapters - │ │ └── Utils - ... - └── testing - └── bsc.contrib - ├── AMBA_Fabrics_AXI4 - ├── AMBA_Fabrics_AXI4_Lite - ... - -What follows is a quick tour. - -// ---------------- -AXI4_Lite/ - - All these have a 'user' field which is not standard in AXI4-Lite - (same as 'user' in AXI4), which can be left unused, and/or set to - width 0. - - AXI4_Lite_Types.bsv - - Definitions for AXI4_Lite bus types, M and S interfaces, - connections between Ms and Ss, dummy M and S tie-offs, and - transactors to provide higher-level FIFO-like interfaces to - drive Ms and Ss. - - Everything is parameterizd on width of address, data and user buses. - - Note: some aspects of these definitions may seem a bit verbose - and messy; that is not typical of BSV code, but is true here - because it is meant to interface to hand-written Verilog, so - we need to provide precise control on interface signal names - and protocols that are required by the Verilog side. Pure BSV - code can be an order of magnitude more compact. - - Everything is parameterized on wd_addr, wd_data, wd_user. - -AXI4_Lite_Fabric.bsv - - Definition for interface and module for an num_M x num_S - crossbar switch with AXI4-Lite interfaces. - - This is also an example of how, within BSV code, we don't - worry about the details of AXI4-Lite signalling. We just - instantiate the transactors defined in AXI4_Lite_Types.bsv, - and then work only with simple, FIFO-like interfaces. - - Everything is parameterized on num_M, num_S, wd_addr, wd_data, - wd_user. - -... clock-crossers, other transactors ... - -// ---------------- -AXI4/ - -AXI4_Types.bsv - -Definitions for AXI4 bus types, M and S interfaces, connections -between Ms and Ss, dummy M and S tie-offs, and transactors to provide -higher-level FIFO-like interfaces to drive Ms and Ss. - -Everything is parameterized on wd_id, wd_addr, wd_data, wd_user. - -Note: some aspects of these definitions may seem a bit verbose and -messy; that is not typical of BSV code, but is true here because it is -meant to interface to hand-written Verilog, so we need to provide -precise control on interface signal names and protocols that are -required by the Verilog side. Pure BSV code can be an order of -magnitude more compact. - -AXI4_Fabric.bsv - -Definition for interface and module for an num_M x num_S crossbar -switch with AXI4 interfaces. - -This is also an example of how, within BSV code, we don't worry about -the details of AXI4-Lite signalling. We just instantiate the -transactors defined in AXI4_Lite_Types.bsv, and then work only with -simple, FIFO-like interfaces. - -Everything is parameterized on num_M, num_S, wd_id, wd_addr, wd_data, -wd_user. - -AXI4_Deburster.bsv - -A module that converts a slave that does not support AXI4 bursts into -a slave that does support bursts. - -... clock-crossers, other transactors - -// ---------------- -Adapters/ - -AXI4L_S_to_AXI4_M_Adapter.bsv - -A module to bridge from an AXI4L S to an AXI4 M. - -AXI4_AXI4_Lite_Adapters.bsv - -// ================================================================ -Dependencies - -Some of these source codes import BSV resources in a sibling directory - - bsc-contrib/Libraries/Misc/ - -// ================================================================ -Build-and-install - -Compiling, Building and Testing - -These are libraries, so you will normally be compiling selected files -from here within some other project that uses these libraries. - -In `AXI4/Unit_Test/` and `AXI4_Lite/Unit_Test/` the source codes and -Makefiles show examples of instantiating fabrics in testbenches and -executing them. - -The Makefiles are set up to build both Bluesim and Verilator -executables. In each case, the Makefile specifies two steps: -compiling BSV source, and then linking into an executable. - -In the Verilator case, the compile step generates Verilog from BSV -source. This Verilog can be used as Verilog AMBA IP in other Verilog -or SystemVerilog projects. - -// ================================================================ diff --git a/Libraries/AMBA_Fabrics/AXI4/AXI_SyncBuffer.bsv b/Libraries/AMBA_Fabrics/Utils/AXIx_SyncBuffer.bsv similarity index 72% rename from Libraries/AMBA_Fabrics/AXI4/AXI_SyncBuffer.bsv rename to Libraries/AMBA_Fabrics/Utils/AXIx_SyncBuffer.bsv index 007b391..439ea6b 100644 --- a/Libraries/AMBA_Fabrics/AXI4/AXI_SyncBuffer.bsv +++ b/Libraries/AMBA_Fabrics/Utils/AXIx_SyncBuffer.bsv @@ -1,17 +1,16 @@ -// Copyright (c) 2021-2022 Bluespec, Inc. All Rights Reserved -// Author: Rishiyur S. Nikhil -// +// Copyright (c) 2021-2024 Bluespec, Inc. All Rights Reserved + // SPDX-License-Identifier: BSD-3-Clause -package AXI_SyncBuffer; +package AXIx_SyncBuffer; // ================================================================ +// This package can be used for both AXI4 and AXI4L. // This package defines a clock-domain-crossing buffer for an AXI bus -// (parameterized, so can be used for AXI4 and AXI4 Lite). -// The interfaces are FIFOF-like (not raw AXI4 signals). -// The module merely contains 5 SyncFIFOs for each of the 5 AXI4 or -// AXI4 Lite channels +// The interfaces are FIFOF-like (not RTL-level AMBA signals). +// The module merely contains 5 standard SyncFIFOs for each of the 5 +// AXI4 or AXI4 Lite channels. // ================================================================ // Bluespec library imports @@ -20,9 +19,8 @@ import Clocks :: *; import Connectable :: *; // ---------------- -// BSV additional libs +// Bluespec misc. libs -import Cur_Cycle :: *; import GetPut_Aux :: *; import Semi_FIFOF :: *; @@ -32,9 +30,18 @@ import Semi_FIFOF :: *; // -- none // ================================================================ -// The interface for the fabric module +// The AXIx_SyncBuffer interface. +// Note: AXIx (not AXI4 or AXI4L) because it can be used for both + +interface AXIx_SyncBuffer_IFC #(type aw, type w, type b, type ar, type r); + interface AXIx_S_IFC #(aw, w, b, ar, r) from_M; + interface AXIx_M_IFC #(aw, w, b, ar, r) to_S; +endinterface + +// ---------------- +// The interface for the SyncBuffer -interface AXI_M_IFC #(type aw, type w, type b, type ar, type r); +interface AXIx_M_IFC #(type aw, type w, type b, type ar, type r); interface FIFOF_O #(aw) o_aw; interface FIFOF_O #(w) o_w; interface FIFOF_I #(b) i_b; @@ -42,7 +49,7 @@ interface AXI_M_IFC #(type aw, type w, type b, type ar, type r); interface FIFOF_I #(r) i_r; endinterface -interface AXI_S_IFC #(type aw, type w, type b, type ar, type r); +interface AXIx_S_IFC #(type aw, type w, type b, type ar, type r); interface FIFOF_I #(aw) i_aw; interface FIFOF_I #(w) i_w; interface FIFOF_O #(b) o_b; @@ -50,11 +57,11 @@ interface AXI_S_IFC #(type aw, type w, type b, type ar, type r); interface FIFOF_O #(r) o_r; endinterface -instance Connectable #(AXI_M_IFC #(aw, w, b, ar, r), - AXI_S_IFC #(aw, w, b, ar, r)); +instance Connectable #(AXIx_M_IFC #(aw, w, b, ar, r), + AXIx_S_IFC #(aw, w, b, ar, r)); - module mkConnection #(AXI_M_IFC #(aw, w, b, ar, r) m, - AXI_S_IFC #(aw, w, b, ar, r) s) (Empty); + module mkConnection #(AXIx_M_IFC #(aw, w, b, ar, r) m, + AXIx_S_IFC #(aw, w, b, ar, r) s) (Empty); mkConnection (m.o_aw, s.i_aw); mkConnection (m.o_w, s.i_w); mkConnection (m.i_b, s.o_b); @@ -63,22 +70,14 @@ instance Connectable #(AXI_M_IFC #(aw, w, b, ar, r), endmodule endinstance -// ================================================================ -// The SyncBuffer interface - -interface AXI_SyncBuffer_IFC #(type aw, type w, type b, type ar, type r); - interface AXI_S_IFC #(aw, w, b, ar, r) from_M; - interface AXI_M_IFC #(aw, w, b, ar, r) to_S; -endinterface - // ================================================================ // The SyncBuffer module -// Implements an AXI (AXI4 or AXI4 Lite) clock-crossing +// Implements an AXIx (AXI4 or AXI4 Lite) clock-crossing -module mkAXI_SyncBuffer #(Integer depth, +module mkAXIx_SyncBuffer #(Integer depth, Clock sClkIn, Reset sRstIn, Clock dClkIn, Reset dRstIn) - (AXI_SyncBuffer_IFC #(aw, w, b, ar, r)) + (AXIx_SyncBuffer_IFC #(aw, w, b, ar, r)) provisos (Bits #(aw, _size_aw_t), Bits #(w, _size_w_t), Bits #(b, _size_b_t), @@ -113,7 +112,7 @@ module mkAXI_SyncBuffer #(Integer depth, // ---------------------------------------------------------------- // INTERFACE - interface from_M = interface AXI_S_IFC; + interface from_M = interface AXIx_S_IFC; interface FIFOF_I i_aw = syncFIFO_to_FIFOF_I (f_aw); interface FIFOF_I i_w = syncFIFO_to_FIFOF_I (f_w); interface FIFOF_O o_b = syncFIFO_to_FIFOF_O (f_b); @@ -121,7 +120,7 @@ module mkAXI_SyncBuffer #(Integer depth, interface FIFOF_O o_r = syncFIFO_to_FIFOF_O (f_r); endinterface; - interface to_S = interface AXI_M_IFC; + interface to_S = interface AXIx_M_IFC; interface FIFOF_O o_aw = syncFIFO_to_FIFOF_O (f_aw); interface FIFOF_O o_w = syncFIFO_to_FIFOF_O (f_w); interface FIFOF_I i_b = syncFIFO_to_FIFOF_I (f_b); diff --git a/Libraries/AMBA_Fabrics/Utils/Makefile b/Libraries/AMBA_Fabrics/Utils/Makefile index 6f6654f..77387d2 100644 --- a/Libraries/AMBA_Fabrics/Utils/Makefile +++ b/Libraries/AMBA_Fabrics/Utils/Makefile @@ -8,9 +8,14 @@ LIBNAME=AMBA_Fabrics/Utils # and defines the install target include ../../common.mk +# Requires files in Misc in Utils +BSCFLAGS += -p $(BUILDDIR)/../../Misc:+ +BSCFLAGS += -p $(BUILDDIR)/../Utils:+ + .PHONY: build build: $(BSC) -u $(BSCFLAGS) ByteLane.bsv + $(BSC) -u $(BSCFLAGS) AXIx_SyncBuffer.bsv .PHONY: clean full_clean clean full_clean: diff --git a/Libraries/Misc/Cur_Cycle.bsv b/Libraries/Misc/Cur_Cycle.bsv index 5f96131..d98a2ad 100644 --- a/Libraries/Misc/Cur_Cycle.bsv +++ b/Libraries/Misc/Cur_Cycle.bsv @@ -34,7 +34,6 @@ ActionValue #(Bit #(32)) cur_cycle = actionvalue function Action fa_debug_show_location (Integer verbosity); action if (verbosity != 0) begin - $display (" %m"); $write (" %0d: ", cur_cycle); end endaction diff --git a/doc/How_to_Contribute.adoc b/doc/How_to_Contribute.adoc index b66ff81..e689dd4 100644 --- a/doc/How_to_Contribute.adoc +++ b/doc/How_to_Contribute.adoc @@ -1,13 +1,13 @@ = How to Contribute to `bsc-contrib` :revnumber: v1.03 -:revdate: 2024-11-21 +:revdate: 2024-12-09 :sectnums: // ================================================================ Let's assume you want to contribute a new library `MyContrib`. All your B-Lang (BSV/BH) sources and documentation should be placed in a -new sub-dir: +new sub-directory: bsc-contrib/Libraries/MyContrib/ @@ -15,7 +15,7 @@ If your BSV/BH code imports any Verilog, it should be placed in: bsc-contrib/Verilog/ -If you also have unit-tests, they should be in a new sub-dir: +If you also have unit tests, they should be in a new sub-directory: bsc-contrib/testing/bsc.contrib/Unit_Test_MyContrib @@ -31,12 +31,13 @@ Request (PR). We suggest the following "flow": -* On GitHub, create a FORK of the original `bsc-contrib` repo, under your own account. +* On GitHub, create a FORK of the original `bsc-contrib` repository, + under your own account. * Clone your FORK to your work area (laptop, desktop, ...). * Create a new BRANCH in your clone of your FORK. * In this new BRANCH (in your clone of your FORK), prepare your contribution, including formatting, copyrights, licenses, - build-and-install Makefiles and optional unit-tests. See the + build-and-install Makefiles and optional unit tests. See the sections below for details. * Commit your BRANCH. @@ -46,7 +47,7 @@ We suggest the following "flow": final commit on top of the most recent `bsc-contrib` commit. * Push the commit from your clone up to your FORK. -* From your FORK, create a PR for the original `bsc-contrib` repo. +* From your FORK, create a PR for the original `bsc-contrib` repository. // ================================================================ == Source text formatting @@ -70,7 +71,7 @@ desired Copyright and License text. Example: // SPDX-License-Identifier: BSD-3-Clause You are responsible for specifying your preferred copyright and -license text (but please remember that this is a public repo; +license text (but please remember that this is a public repository; everything here is expected to be free and open-source; anything here should be freely usable in commercial products). @@ -91,24 +92,25 @@ AsciiDoctor (`.adoc`) are automatically rendered by GitHub. // ================================================================ == Makefiles for building and installing -`make all` at the top level descends recursively into all sub-dirs of -`Libraries` and `Verilog`. In a leaf-level library dir the Makefile -contains a `build` target to compile source file(s) that are in that -dir. The corresponding object files get copied into the installation -directory (default `bsc-contrib/inst`). You can also specify other -files to be copied (include files, config files, ...). +`make all` at the top level descends recursively into all +sub-directories of `Libraries` and `Verilog`. In a leaf-level library +directory the `Makefile` contains a `build` target to compile source +file(s) that are in that directory. The corresponding object files +get copied into the installation directory (default +`bsc-contrib/inst`). You can also specify other files to be copied +(include files, config files, ...). -If you added any files to the `Verilog/` dir, add those filenames to -the `VERI_FILES` list in the `Verilog/Makefile`. +If you added any files to the `Verilog/` directory, add those +filenames to the `VERI_FILES` list in the `Verilog/Makefile`. The top-level `make all` is performed for installation, but is also repeated automatically during CI (continuous integration) to check that it succeeds. -NOTE: In the `Library` sub-dirs, this just compiles sources, it does - not build executables. See separate "Unit Tests" section for - building unit-test executables and running them. Those are also - performed by CI. +NOTE: In the `Library` sub-directories, this just compiles sources, it + does not build executables. See separate "Unit Tests" section + for building unit test executables and running them. Those are + also performed by CI. // ---------------------------------------------------------------- @@ -121,9 +123,9 @@ In the existing add `MyContrib` to the `BUILD_ORDER` list in order to include your contribution into the recursive descent. -If `Libraries/MyContrib/` has sub-dirs, repeat the recursive pattern -of `Libraries/Makefile` to enable recursive descent into each sub-dir, -as needed. Example: +If `Libraries/MyContrib/` has sub-directories, repeat the recursive +pattern of `Libraries/Makefile` to enable recursive descent into each +sub-directory, as needed. Example: Libraries/AMBA_TLM2/Makefile @@ -153,9 +155,9 @@ your copy (clone of fork) of `bsc-contrib`: $ cd bsc-contrib // my clone of my fork $ make all -This should run the `make install` action on the whole repo, including -compiling your sources in your newly added `MyContrib`, and installing -the corresponding object files in: +This should run the `make install` action on the whole repository, +including compiling your sources in your newly added `MyContrib`, and +installing the corresponding object files in: bsc-contrib/inst/lib/Libraries/MyContrib/ @@ -166,29 +168,34 @@ You can optionally add unit tests for your library source files; these unit tests are run automatically and repeatedly as part of CI (Continuous Integration). -NOTE: `bsc-contrib` 's unit-testing is performed as part of the main +NOTE: `bsc-contrib` 's unit testing is performed as part of the main `bsc` compiler's testing of standard libraries, using the same - infrastructure. The infrastructure has more ways to configure - testing than described in this section; please see the `bsc` - repo for more details (https://github.com/B-Lang-org/bsc). - -Add your unit tests to `bsc-contrib/testing/bsc.contrib/`. You can -study any of the existing unit tests in that directory for guidance. + infrastructure. The infrastructure has many more ways to + configure testing than the brief description here; please see + `testsuite/README.md` in the `bsc` repository for more details + (https://github.com/B-Lang-org/bsc). + +Add your unit tests to `bsc-contrib/testing/bsc.contrib/`. For +guidance, you can study any of the existing unit tests in that +directory or in `testsuite/` in the `bsc` compiler repository. Briefly: -* Add a sub-dir for a new set of unit-tests for the new library (it - can have sub-dirs for more detailed structure): +* Add a sub-directory for a new set of unit tests for the new library + (it can have sub-directories for more detailed structure): bsc-contrib/testing/bsc.contrib/MyContrib/ -* In this dir: +* In this directory: + +** There should be a `Makefile` that is just boilerplate; see any of + the other unit test directories for examples. -** Each top-level test driver module should be in a file `Foo.bsv` or - `Foo.bs`, and the module should be called `sysFoo`, with an `Empty` - interface. +** You can have multiple test programs. Each such top-level `Foo.bsv` + (or `Foo.bs`) should contain a top-level module `sysFoo` with an + `Empty` interface. -** If the top-level file imports other support files (just for this - test, not library files), they can be placed here, too. +** If the top-level file imports other support source files (just for + this test, not library files), they can be placed here, too. ** Create a file `sysFoo.out.expected` containing output expected when it is run. @@ -200,6 +207,16 @@ If different output is expected from Bluesim vs. Verilog sim (e.g., *** for Bluesim: `sysFoo.c.out.expected` *** for Verilog: `sysFoo.v.out.expected` +** Finally, a file `foo.exp` contains a fragment of a "script" to be + run in this directory. It can invoke multiple tests in this + directory. Each test can be run in Bluesim or Verilog sim or both. + Again, see other existing unit tests for examples. ++ +For unit + tests included in the `.exp` file, try to keep the run-time short + (no more than about a minute) when run in iverilog, to limit the + overall run-time of CI (Continuous Integration). + // ---------------------------------------------------------------- === Running unit tests @@ -208,7 +225,8 @@ Please run your tests before submitting a PR. See the "Testing" section of the link:../README.md[README] in `bsc-contrib` for information on how to run them (you have to copy -`testing/bsc.contrib` into the `bsc` repo and run it there). +`testing/bsc.contrib` from here into the `bsc` repository's +`testsuite` directory and run it there). NOTE: (Future restructuring plans) + We would like unit tests for @@ -217,6 +235,6 @@ NOTE: (Future restructuring plans) + `testing/bsc.contrib/MyContrib`. The current structure exists because it is derived from historical roots where `bsc-contrib` 's testing was done along with `bsc` 's testing using shared - infrastructure in the `bsc` repo. + infrastructure in the `bsc` repository. // ================================================================ diff --git a/doc/How_to_Contribute.html b/doc/How_to_Contribute.html index ec4eef3..bf1025d 100644 --- a/doc/How_to_Contribute.html +++ b/doc/How_to_Contribute.html @@ -439,7 +439,7 @@

How to Contribute to bsc-contrib

version v1.03, -2024-11-21 +2024-12-09
@@ -448,7 +448,7 @@

How to Contribute to bsc-contrib

Let’s assume you want to contribute a new library MyContrib. All your B-Lang (BSV/BH) sources and documentation should be placed in a -new sub-dir:

+new sub-directory:

@@ -464,7 +464,7 @@

How to Contribute to bsc-contrib

-

If you also have unit-tests, they should be in a new sub-dir:

+

If you also have unit tests, they should be in a new sub-directory:

@@ -500,7 +500,8 @@

1. Logistics

  • -

    On GitHub, create a FORK of the original bsc-contrib repo, under your own account.

    +

    On GitHub, create a FORK of the original bsc-contrib repository, +under your own account.

  • Clone your FORK to your work area (laptop, desktop, …​).

    @@ -511,7 +512,7 @@

    1. Logistics

  • In this new BRANCH (in your clone of your FORK), prepare your contribution, including formatting, copyrights, licenses, -build-and-install Makefiles and optional unit-tests. See the +build-and-install Makefiles and optional unit tests. See the sections below for details.

  • @@ -527,7 +528,7 @@

    1. Logistics

    Push the commit from your clone up to your FORK.

  • -

    From your FORK, create a PR for the original bsc-contrib repo.

    +

    From your FORK, create a PR for the original bsc-contrib repository.

@@ -574,7 +575,7 @@

You are responsible for specifying your preferred copyright and -license text (but please remember that this is a public repo; +license text (but please remember that this is a public repository; everything here is expected to be free and open-source; anything here should be freely usable in commercial products).

@@ -606,16 +607,17 @@

4. README

5. Makefiles for building and installing

-

make all at the top level descends recursively into all sub-dirs of -Libraries and Verilog. In a leaf-level library dir the Makefile -contains a build target to compile source file(s) that are in that -dir. The corresponding object files get copied into the installation -directory (default bsc-contrib/inst). You can also specify other -files to be copied (include files, config files, …​).

+

make all at the top level descends recursively into all +sub-directories of Libraries and Verilog. In a leaf-level library +directory the Makefile contains a build target to compile source +file(s) that are in that directory. The corresponding object files +get copied into the installation directory (default +bsc-contrib/inst). You can also specify other files to be copied +(include files, config files, …​).

-

If you added any files to the Verilog/ dir, add those filenames to -the VERI_FILES list in the Verilog/Makefile.

+

If you added any files to the Verilog/ directory, add those +filenames to the VERI_FILES list in the Verilog/Makefile.

The top-level make all is performed for installation, but is also @@ -629,10 +631,10 @@

5. Makefiles for building and in
Note
-In the Library sub-dirs, this just compiles sources, it does - not build executables. See separate "Unit Tests" section for - building unit-test executables and running them. Those are also - performed by CI. +In the Library sub-directories, this just compiles sources, it + does not build executables. See separate "Unit Tests" section + for building unit test executables and running them. Those are + also performed by CI. @@ -652,9 +654,9 @@

5.1. Setting u contribution into the recursive descent.

-

If Libraries/MyContrib/ has sub-dirs, repeat the recursive pattern -of Libraries/Makefile to enable recursive descent into each sub-dir, -as needed. Example:

+

If Libraries/MyContrib/ has sub-directories, repeat the recursive +pattern of Libraries/Makefile to enable recursive descent into each +sub-directory, as needed. Example:

@@ -711,9 +713,9 @@

5.2. Running build-and-install

-

This should run the make install action on the whole repo, including -compiling your sources in your newly added MyContrib, and installing -the corresponding object files in:

+

This should run the make install action on the whole repository, +including compiling your sources in your newly added MyContrib, and +installing the corresponding object files in:

@@ -738,25 +740,27 @@

6. Optional Unit Tests

Note
-bsc-contrib 's unit-testing is performed as part of the main +bsc-contrib 's unit testing is performed as part of the main bsc compiler’s testing of standard libraries, using the same - infrastructure. The infrastructure has more ways to configure - testing than described in this section; please see the bsc - repo for more details (https://github.com/B-Lang-org/bsc). + infrastructure. The infrastructure has many more ways to + configure testing than the brief description here; please see + testsuite/README.md in the bsc repository for more details + (https://github.com/B-Lang-org/bsc).
-

Add your unit tests to bsc-contrib/testing/bsc.contrib/. You can -study any of the existing unit tests in that directory for guidance. +

Add your unit tests to bsc-contrib/testing/bsc.contrib/. For +guidance, you can study any of the existing unit tests in that +directory or in testsuite/ in the bsc compiler repository. Briefly:

  • -

    Add a sub-dir for a new set of unit-tests for the new library (it -can have sub-dirs for more detailed structure):

    +

    Add a sub-directory for a new set of unit tests for the new library +(it can have sub-directories for more detailed structure):

    bsc-contrib/testing/bsc.contrib/MyContrib/
    @@ -764,17 +768,21 @@

    6. Optional Unit Tests

  • -

    In this dir:

    +

    In this directory:

    • -

      Each top-level test driver module should be in a file Foo.bsv or -Foo.bs, and the module should be called sysFoo, with an Empty -interface.

      +

      There should be a Makefile that is just boilerplate; see any of +the other unit test directories for examples.

    • -

      If the top-level file imports other support files (just for this -test, not library files), they can be placed here, too.

      +

      You can have multiple test programs. Each such top-level Foo.bsv +(or Foo.bs) should contain a top-level module sysFoo with an +Empty interface.

      +
    • +
    • +

      If the top-level file imports other support source files (just for +this test, not library files), they can be placed here, too.

    • Create a file sysFoo.out.expected containing output expected when @@ -795,6 +803,18 @@

      6. Optional Unit Tests

  • +
  • +

    Finally, a file foo.exp contains a fragment of a "script" to be +run in this directory. It can invoke multiple tests in this +directory. Each test can be run in Bluesim or Verilog sim or both. +Again, see other existing unit tests for examples.

    +
    +

    For unit + tests included in the .exp file, try to keep the run-time short + (no more than about a minute) when run in iverilog, to limit the + overall run-time of CI (Continuous Integration).

    +
    +
@@ -808,7 +828,8 @@

6.1. Running unit tests

See the "Testing" section of the README in bsc-contrib for information on how to run them (you have to copy -testing/bsc-contrib into the bsc repo and run it there).

+testing/bsc.contrib from here into the bsc repository’s +testsuite directory and run it there).

@@ -824,7 +845,7 @@

6.1. Running unit tests

testing/bsc.contrib/MyContrib. The current structure exists because it is derived from historical roots where bsc-contrib 's testing was done along with bsc 's testing using shared - infrastructure in the bsc repo. + infrastructure in the bsc repository.
@@ -836,7 +857,7 @@

6.1. Running unit tests

diff --git a/testing/bsc.contrib/AMBA_Fabrics/AXI4/Makefile b/testing/bsc.contrib/AMBA_Fabrics/AXI4/Makefile new file mode 100644 index 0000000..607fb8d --- /dev/null +++ b/testing/bsc.contrib/AMBA_Fabrics/AXI4/Makefile @@ -0,0 +1,5 @@ +# for "make clean" to work everywhere + +CONFDIR = $(realpath ../../..) + +include $(CONFDIR)/clean.mk diff --git a/testing/bsc.contrib/AMBA_Fabrics/AXI4/Test_AXI4_Deburster.bsv b/testing/bsc.contrib/AMBA_Fabrics/AXI4/Test_AXI4_Deburster.bsv new file mode 100644 index 0000000..e8df85d --- /dev/null +++ b/testing/bsc.contrib/AMBA_Fabrics/AXI4/Test_AXI4_Deburster.bsv @@ -0,0 +1,282 @@ +// Copyright (c) 2019-2023 Bluespec, Inc. All Rights Reserved +// Copyright (c) 2024 Rishiyur S. Nikhil. + +// SPDX-License-Identifier: BSD-3-Clause + +package Test_AXI4_Deburster; + +// **************************************************************** +// Standalone unit tester for AXI4_Deburster.bsv + +// **************************************************************** +// Bluespec library imports + +import FIFOF :: *; +import Connectable :: *; + +// ---------------- +// BSV additional libs + +import Cur_Cycle :: *; +import Semi_FIFOF :: *; + +// ================================================================ +// Project imports + +import AXI4_Types :: *; +import AXI4_Deburster :: *; + +// **************************************************************** +// Synthesized instance of Deburster + +typedef 4 Wd_Id; +typedef 32 Wd_Addr; +typedef 64 Wd_Data; +typedef 10 Wd_User; + +// ================================================================ + +(* synthesize *) +module sysTest_AXI4_Deburster (Empty); + + // Buffer for downstream logic + AXI4_Buffer_IFC #(Wd_Id, Wd_Addr, Wd_Data, Wd_User) s <- mkAXI4_Buffer; + + // Deburster, connected to downstream + AXI4_S_IFC #(Wd_Id, Wd_Addr, Wd_Data, Wd_User) deburster <- mkAXI4_Deburster (s.ifc_S); + + Reg #(Bit #(32)) rg_test <- mkReg (20); // Chooses which test to run + FIFOF #(Bit #(8)) f_len <- mkFIFOF; + Reg #(Bit #(8)) rg_beat <- mkReg (0); + Reg #(Bit #(32)) rg_idle_count <- mkReg (0); + + // ================================================================ + // Help function to create AXI4 channel payloads + + function AXI4_AW #(Wd_Id, Wd_Addr, Wd_User) + fv_mkAW (Bit #(Wd_Id) id, + Bit #(Wd_Addr) addr, + Bit #(8) len, + Bit #(2) burst, + Bit #(Wd_User) user); + return AXI4_AW {awid: id, + awaddr: addr, + awlen: len, + awsize: axsize_8, + awburst: burst, + awlock: 0, + awcache: 0, + awprot: 0, + awqos: 0, + awregion: 0, + awuser: user}; + endfunction + + function AXI4_W #(Wd_Data, Wd_User) + fv_mkW (Bit #(Wd_Data) data, + Bit #(Wd_User) user); + Bool last = (rg_beat == f_len.first - 1); + return AXI4_W {wdata: data, + wstrb: 'hFF, + wlast: last, + wuser: user}; + endfunction + + function AXI4_B #(Wd_Id, Wd_User) + fv_mkB (AXI4_AW #(Wd_Id, Wd_Addr, Wd_User) aw); + return AXI4_B {bid: aw.awid, + bresp: axi4_resp_okay, + buser: aw.awuser}; + endfunction + + function AXI4_AR #(Wd_Id, Wd_Addr, Wd_User) + fv_mkAR (Bit #(Wd_Id) id, + Bit #(Wd_Addr) addr, + Bit #(8) len, + Bit #(2) burst, + Bit #(Wd_User) user); + return AXI4_AR {arid: id, + araddr: addr, + arlen: len, + arsize: axsize_8, + arburst: burst, + arlock: 0, + arcache: 0, + arprot: 0, + arqos: 0, + arregion: 0, + aruser: user}; + endfunction + + function AXI4_R #(Wd_Id, Wd_Data, Wd_User) + fv_mkR (AXI4_AR #(Wd_Id, Wd_Addr, Wd_User) ar); + return AXI4_R {rid: ar.arid, + rdata: zeroExtend (ar.araddr + 'h10_000), + rresp: axi4_resp_okay, + rlast: True, + ruser: ar.aruser}; + endfunction + + // ================================================================ + // STIMULUS + + Bit #(Wd_Id) id1 = 1; + Bit #(Wd_User) user1 = 1; + + // ---------------- + // Write tests + + rule rl_wr_single (rg_test == 0); + Bit #(8) len = 1; + let aw = fv_mkAW (id1, 'h1000, (len - 1), axburst_fixed, user1); + deburster.i_AW.enq (aw); + + f_len.enq (len); + rg_idle_count <= 0; + rg_test <= 100; + + $display ("%0d: M.rl_wr_single", cur_cycle); + $display (" ", fshow (aw)); + endrule + + rule rl_wr_burst_addr_0 (rg_test == 10); + Bit #(8) len = 2; + let aw = fv_mkAW (id1, 'h1000, (len - 1), axburst_incr, user1); + deburster.i_AW.enq (aw); + + f_len.enq (len); + rg_idle_count <= 0; + rg_test <= 11; + + $display ("%0d: M.rl_wr_burst_addr_0", cur_cycle); + $display (" ", fshow (aw)); + endrule + + rule rl_wr_burst_addr_1 (rg_test == 11); + Bit #(8) len = 4; + let aw = fv_mkAW (id1, 'h2000, (len - 1), axburst_incr, user1); + deburster.i_AW.enq (aw); + + f_len.enq (len); + rg_idle_count <= 0; + rg_test <= 100; + + $display ("%0d: M.rl_wr_burst_addr_1", cur_cycle); + $display (" ", fshow (aw)); + endrule + + rule rl_wr_data; + let data = 'h1_0000 + zeroExtend (rg_beat); + let wd = fv_mkW (data, user1); + deburster.i_W.enq (wd); + rg_idle_count <= 0; + + if (rg_beat < f_len.first - 1) + rg_beat <= rg_beat + 1; + else begin + rg_beat <= 0; + f_len.deq; + + rg_test <= '1; + end + + $display ("%0d: M.rl_wr_data", cur_cycle); + $display (" ", fshow (wd)); + endrule + + // ---------------- + // Read tests + + rule rl_rd_single (rg_test == 2); + let ar = fv_mkAR (id1, 'h1000, 1, axburst_fixed, user1); + deburster.i_AR.enq (ar); + rg_idle_count <= 0; + rg_test <= '1; + + $display ("%0d: M.rl_rd_single", cur_cycle); + $display (" ", fshow (ar)); + endrule + + rule rl_rd_burst_addr_0 (rg_test == 20); + Bit #(8) len = 2; + let ar = fv_mkAR (id1, 'h1000, (len - 1), axburst_incr, user1); + deburster.i_AR.enq (ar); + + rg_idle_count <= 0; + rg_test <= 21; + + $display ("%0d: M.rl_rd_burst_addr_0", cur_cycle); + $display (" ", fshow (ar)); + endrule + + rule rl_rd_burst_addr_1 (rg_test == 21); + Bit #(8) len = 4; + let ar = fv_mkAR (id1, 'h2000, (len - 1), axburst_incr, user1); + deburster.i_AR.enq (ar); + + rg_idle_count <= 0; + rg_test <= 100; + + $display ("%0d: M.rl_rd_burst_addr_1", cur_cycle); + $display (" ", fshow (ar)); + endrule + + // ================================================================ + // Drain and display responses received by M + + rule rl_wr_resps; + let wr_resp <- pop_o (deburster.o_B); + $display ("%0d: M.rl_wr_resps", cur_cycle); + $display (" ", fshow (wr_resp)); + rg_idle_count <= 0; + endrule + + rule rl_rd_resps; + let rd_resp <- pop_o (deburster.o_R); + $display ("%0d: M.rl_rd_resps", cur_cycle); + $display (" ", fshow (rd_resp)); + rg_idle_count <= 0; + endrule + + // ================================================================ + // S: return functional responses + // Note: we should not be receiving any bursts, since we're fronted by the Deburster. + + rule rl_S_IP_model_writes; + $display ("%0d: S.rl_S_IP_model_writes", cur_cycle); + + let aw <- pop_o (s.ifc_M.o_AW); + let w <- pop_o (s.ifc_M.o_W); + + let b = fv_mkB (aw); + s.ifc_M.i_B.enq (b); + $display (" ", fshow (aw)); + $display (" ", fshow (w)); + $display (" ", fshow (b)); + endrule + + rule rl_S_IP_model_AR; + let ar <- pop_o (s.ifc_M.o_AR); + s.ifc_M.i_R.enq (fv_mkR (ar)); + + $display ("%0d: S.rl_S_IP_model_AR", cur_cycle); + $display (" ", fshow (ar)); + endrule + + // ================================================================ + + rule rl_idle_quit; + if (rg_idle_count == 100) begin + $display ("%0d: rl_idle_quit", cur_cycle); + $finish (0); + end + else begin + rg_idle_count <= rg_idle_count + 1; + end + endrule + +endmodule + +// ================================================================ + +endpackage diff --git a/testing/bsc.contrib/AMBA_Fabrics/AXI4/Test_AXI4_Fabric.bsv b/testing/bsc.contrib/AMBA_Fabrics/AXI4/Test_AXI4_Fabric.bsv new file mode 100644 index 0000000..1016a2e --- /dev/null +++ b/testing/bsc.contrib/AMBA_Fabrics/AXI4/Test_AXI4_Fabric.bsv @@ -0,0 +1,649 @@ +// Copyright (c) 2021-2023 Bluespec, Inc. All Rights Reserved +// Copyright (c) 2024 Rishiyur S. Nikhil. + +// SPDX-License-Identifier: BSD-3-Clause + +package Test_AXI4_Fabric; + +// ================================================================ +// Standalone unit tester for AXI4_Fabric.bsv + +// The top-level module is: sysTest_AXI4_Fabric + +// Instantiates a 2-M, 3-S AXI4 fabric. + +// Instantiates 2-M test-generators, connected to fabric M ports 0,1. +// Instantiates 3-S modules, connected to fabric S ports 0,1,2. + +// Each M generates 'num_xactions' read and write requests containing, +// in the following fields: +// axid source id (0,1) +// axaddr a random address +// axuser User_struct { wild, M_num, S_num, serial_num } +// AW transactions send W.data = AW.awaddr+1 + +// Ss (and the fabric, if fabric error) copy awuser into buser, aruser into ruser +// Ss check s_num +// Ss check aw.addr+1 = w.data for AW/W transactions + +// Ms check buser/ruser +// Ms check R responses for addr+1=data + +// POTENTIAL IMPROVEMENTS: +// * Currently does not generate/test bursts (all transactions are single-beat) + +// ================================================================ +// Bluespec library imports + +import FIFOF :: *; +import Vector :: *; +import Connectable :: *; +import LFSR :: *; + +// ---------------- +// BSV additional libs + +import Semi_FIFOF :: *; +import GetPut_Aux :: *; // for FIFO 'pop' + +// ================================================================ +// Project imports + +import AXI4_Types :: *; +import AXI4_Fabric :: *; + +// **************************************************************** +// Number of rd/wr transactions to generate (by each M). +// This number chosen for reasonable runtime in iverilog. + +Integer num_xactions = 10000; + +// ================================================================ +// Verbosity during simulation on stdout (edit this as desired): +// 0: quiet +// 1: show xactions brief +// 2: show xactions detail + +Integer verbosity = 0; + +// ================================================================ +// Fabric parameters + +typedef 2 Num_Ms; +typedef 3 Num_Ss; + +typedef TLog #(Num_Ms) Wd_M_Num; +typedef Bit #(Wd_M_Num) M_Num; + +typedef TLog #(Num_Ss) Wd_S_Num; +typedef Bit #(Wd_S_Num) S_Num; + +// ---------------- +// Address map of the three memory units +// Note: requests to gaps should return error responses + +// Mem unit 0 +Integer addr_base_0 = 'h_0000_0000; +Integer addr_lim_0 = 'h_0100_0000 - 'h_0000_1000; // size 16MB - 4KB + +// Gap of 4KB + +// Mem unit 1 +Integer addr_base_1 = 'h_0100_0000; +Integer addr_lim_1 = 'h_0180_0000; // size 8MB + +// Mem unit 2 +Integer addr_base_2 = 'h_0180_0000; +Integer addr_lim_2 = 'h_01C0_0000; // size 4MB + +// Gap to 'h_FFFF_FFFF (rest of addr space + +Integer addr_msb = 24; // [24:0] + +// ================================================================ +// AXI4 parameters + +typedef Bit #(20) Serial_Num; // per-M xaction serial number + +typedef 4 Wd_Id_M; // M number +typedef TAdd #(Wd_Id_M, TLog #(Num_Ms)) Wd_Id_S; +typedef 25 Wd_Addr; // to accomodate upto addr_lim_X +typedef 32 Wd_Data; // carries addr+1, for testing + +// 'user' field in AXI4 responses contain this struct +typedef struct { + Bool wild; + Bit #(TLog #(Num_Ms)) m_num; + Bit #(TLog #(Num_Ss)) s_num; + Bit #(Wd_Addr) addr; + Serial_Num serial_num; +} User_struct +deriving (Bits, FShow); + +typedef SizeOf #(User_struct) Wd_User; + +// More compact output than fshow +function Fmt fmt_User_struct (User_struct u); + Fmt f = $format ("User{m%0d", u.m_num); + if (u.wild) + f = f + $format (" wild"); + else + f = f + $format (" s%0d", u.s_num); + f = f + $format (" addr:%0h}", u.addr); + f = f + $format (" #0x%0h}", u.serial_num); + return f; +endfunction + +AXI4_Size axsize_full1 = axsize_32; // full width of data bus + +// **************************************************************** +// Routing function used by crossbar switch + +function Tuple2 #(Bool, S_Num) + fn_addr_to_S_num (Bit #(Wd_Addr) addr); + if ((fromInteger (addr_base_0) <= addr) && (addr < fromInteger (addr_lim_0))) + return tuple2 (True, 0); + + else if ((fromInteger (addr_base_1) <= addr) && (addr < fromInteger (addr_lim_1))) + return tuple2 (True, 1); + + else if ((fromInteger (addr_base_2) <= addr) && (addr < fromInteger (addr_lim_2))) + return tuple2 (True, 2); + + else + return tuple2 (False, ?); +endfunction + +// **************************************************************** +// M box for stimulus generation + +interface Stim_IFC; + interface AXI4_M_IFC #(Wd_Id_M, Wd_Addr, Wd_Data, Wd_User) ifc_M; + method Bool completed; + method ActionValue #(Bool) print_stats; +endinterface + +// ---------------------------------------------------------------- +// Help functions to create AXI4 packets + +function AXI4_AW #(Wd_Id_M, Wd_Addr, Wd_User) + fv_mkAW (Bit #(Wd_Id_M) id, Bit #(Wd_Addr) addr, Bit #(Wd_User) user); + + return AXI4_AW {awid: id, + awaddr: addr, + awlen: 0, // 1 beat + awsize: axsize_full1, + awburst: axburst_incr, + awlock: axlock_normal, + awcache: awcache_norm_noncache_nonbuf, + awprot: 0, + awqos: 0, + awregion: 0, + awuser: user}; +endfunction + +function AXI4_AR #(Wd_Id_M, Wd_Addr, Wd_User) + fv_mkAR (Bit #(Wd_Id_M) id, Bit #(Wd_Addr) addr, Bit #(Wd_User) user); + + return AXI4_AR {arid: id, + araddr: addr, + arlen: 0, // 1 beat + arsize: axsize_full1, + arburst: axburst_incr, + arlock: axlock_normal, + arcache: arcache_norm_noncache_nonbuf, + arprot: 0, + arqos: 0, + arregion: 0, + aruser: user}; +endfunction + +function AXI4_W #(Wd_Data, Wd_User) + fv_mkW (Bit #(Wd_Data) data, Bit #(Wd_User) user); + + return AXI4_W {wdata: data, + wstrb: '1, // all ones + wlast: True, // Last beat in burst + wuser: user}; +endfunction + +function AXI4_R #(Wd_Id_S, Wd_Data, Wd_User) + fv_mkR (Bit #(Wd_Id_S) id, Bit #(Wd_Data) data, Bit #(Wd_User) user); + return AXI4_R {rid: id, + rdata: data, + rresp: axi4_resp_okay, + rlast: True, // Last beat in burst + ruser: user}; +endfunction + +function AXI4_B #(Wd_Id_S, Wd_User) + fv_mkB (Bit #(Wd_Id_S) req_id, Bit #(Wd_User) req_user); + return AXI4_B {bid: req_id, + bresp: axi4_resp_okay, + buser: req_user}; +endfunction + +// ---------------------------------------------------------------- +// An M box for stimulus and responses +// This M box generates 'num_xactions' random AXI4 requests +// and (concurrently) processes the AXI4 responses. + +(* synthesize *) +module mkMbox #(parameter Bit #(4) id) (Stim_IFC); + + // Transactor for interface + AXI4_Buffer_IFC #(Wd_Id_M, Wd_Addr, Wd_Data, Wd_User) buf_M <- mkAXI4_Buffer; + + // Pseudo-random number generator + LFSR #(Bit #(32)) lfsr_a <- mkLFSR_32; + + // When we gen AW, this queue holds info to generate corresponding W + FIFOF #(Tuple2 #(Bit #(Wd_Data), Bit #(Wd_User))) f_W_info <- mkFIFOF; + + // Transaction serial number + Reg #(Serial_Num) rg_serial_num <- mkReg (0); + + // Statistics + Reg #(Bit #(32)) rg_num_AR <- mkReg (0); // read requests + Reg #(Bit #(32)) rg_num_AW <- mkReg (0); // write requests + Reg #(Bit #(32)) rg_num_AR_wild <- mkReg (0); // read/write reqs to wild addrs + Reg #(Bit #(32)) rg_num_AW_wild <- mkReg (0); // read/write reqs to wild addrs + Reg #(Bit #(32)) rg_num_R <- mkReg (0); // read responses + Reg #(Bit #(32)) rg_num_B <- mkReg (0); // write responses + + // ---------------- + // FIFOs of request serial numbers to check against responses. + // Because responses from different Ss may return in a different order, + // we keep separate FIFOs for each S. + // Size of FIFOs should be > latency to S and back. + + // Reads + Vector #(Num_Ss, FIFOF #(Serial_Num)) + vf_rd_serial_nums <- replicateM (mkSizedFIFOF (256)); + // Writes + Vector #(Num_Ss, FIFOF #(Serial_Num)) + vf_wr_serial_nums <- replicateM (mkSizedFIFOF (256)); + + // ---------------------------------------------------------------- + // Initialize random num generator + + rule rl_init_LFSR (rg_serial_num == 0); + Vector #(32, Bit #(TAdd #(Wd_Id_M,1))) v = replicate ({id,1'b1}); + Bit #(32) x = truncate (pack (v)); + if (verbosity != 0) + $display ("M%0d: lfsr seed is %0h", x); + + lfsr_a.seed (x); + rg_serial_num <= 1; + endrule + + // ================================================================ + // Request generation + + // Generate read and write requests + rule rl_AR_AW ((0 < rg_serial_num) && (rg_serial_num <= fromInteger (num_xactions))); + let a32 = lfsr_a.value; lfsr_a.next; + Bit #(Wd_Addr) addr = truncate (a32); + + // Compute 'user' field + match { .routable, .s_num } = fn_addr_to_S_num (addr); + Bool wild = (! routable); + let u_struct = User_struct {wild: wild, + m_num: truncate (id), + s_num: s_num, + addr: addr, + serial_num: rg_serial_num}; + Bit #(Wd_User) u = pack (u_struct); + + // Read transactions + if (a32 [31] == 0) begin + let ar = fv_mkAR (id, addr, u); + buf_M.ifc_S.i_AR.enq (ar); + + if (! wild) begin + vf_rd_serial_nums [s_num].enq (rg_serial_num); + rg_num_AR <= rg_num_AR + 1; + end + else + rg_num_AR_wild <= rg_num_AR_wild + 1; + + if (verbosity == 1) begin + $display ("M%0d: ", id, fshow_AR (ar)); + $display ("M%0d: ", id, fmt_User_struct (u_struct)); + end + else if (verbosity > 1) begin + $display ("M%0d: ", id, fshow (ar)); + $display ("M%0d: ", id, fmt_User_struct (u_struct)); + end + end + + // Write transactions + else begin + let aw = fv_mkAW (id, addr, u); + buf_M.ifc_S.i_AW.enq (aw); + + // Enqueue a request to produce W. + // The data is addr+1, which will be checked by S + Bit #(Wd_Data) d = zeroExtend (addr) + 1; + f_W_info.enq (tuple2 (d, u)); + + if (! wild) begin + vf_wr_serial_nums [s_num].enq (rg_serial_num); + rg_num_AW <= rg_num_AW + 1; + end + else + rg_num_AW_wild <= rg_num_AW_wild + 1; + + if (verbosity == 1) begin + $display ("M%0d: ", id, fshow_AW (aw)); + $display ("M%0d: ", id, fmt_User_struct (u_struct)); + end + else if (verbosity > 1) begin + $display ("M%0d: ", id, fshow (aw)); + $display ("M%0d: ", id, fmt_User_struct (u_struct)); + end + end + rg_serial_num <= rg_serial_num + 1; + endrule + + // ---------------- + // M: write data for write-transaction + + rule rl_W; + match { .data, .user } <- pop (f_W_info); + + let w = fv_mkW (data, user); + buf_M.ifc_S.i_W.enq (w); + + User_struct u_struct = unpack (user); + if (verbosity == 1) begin + $display ("M%0d: ", id, fshow_W (w)); + $display ("M%0d: ", id, fmt_User_struct (u_struct)); + end + else if (verbosity > 1) begin + $display ("M%0d: ", id, fshow (w)); + $display ("M%0d: ", id, fmt_User_struct (u_struct)); + end + endrule + + // ================================================================ + // Response collection and checking + + // ---------------------------------------------------------------- + // Response-checker function + + function ActionValue #(Bool) + fav_check_resp (String rd_wr_s, + Bit #(Wd_Id_M) rsp_id, + AXI4_Resp rsp_resp, + Bit #(Wd_User) rsp_user, + Vector #(Num_Ss, FIFOF #(Serial_Num)) vf_serial_nums); + actionvalue + User_struct user_struct = unpack (rsp_user); + + let wild = user_struct.wild; + let rsp_s_num = user_struct.s_num; + let rsp_serial = user_struct.serial_num; + + Bool err = False; + + // Check if routed correctly (routable) + Bool addr_ok = (((! wild) && (rsp_resp == axi4_resp_okay)) + || (wild && (rsp_resp != axi4_resp_okay))); + if (! addr_ok) begin + $display ("M%0d: ERROR: %s wild:", id, rd_wr_s, fshow (wild), + " but resp:", fshow_AXI4_Resp (rsp_resp)); + err = True; + end + else if (id != rsp_id) begin + // Check if response returned to correct sender (id) + $display ("M%0d: ERROR: %s id (%0d) != rsp_id", id, rd_wr_s, id, rsp_id); + err = True; + end + else if (! wild) begin + // Check serial number + let exp_serial <- pop (vf_serial_nums [rsp_s_num]); + if (exp_serial != rsp_serial) begin + $display ("M%0d: ERROR: %s ser num mismatch: expected 0x%0x; response 0x%0x", + id, rd_wr_s, exp_serial, rsp_serial); + err = True; + end + end + + return err; + endactionvalue + endfunction + + // Collect R response; display and check + rule rl_R; + let r <- pop_o (buf_M.ifc_S.o_R); + rg_num_R <= rg_num_R + 1; + + User_struct u_struct = unpack (r.ruser); + if (verbosity == 1) begin + $display (" M%0d: ", id, fshow_R (r)); + $display (" M%0d: ", id, fmt_User_struct (u_struct)); + end + + let err <- fav_check_resp ("R", r.rid, r.rresp, r.ruser, vf_rd_serial_nums); + + if ((r.rresp == axi4_resp_okay) + && (r.rdata != zeroExtend (u_struct.addr) + 1)) begin + $display (" M%0d: ERROR: R data != addr+1"); + err = True; + end + + if (err || verbosity > 1) begin + $display (" M%0d: ", id, fshow (r)); + $display (" M%0d: ", id, fmt_User_struct (u_struct)); + end + + if (err) begin + $display ("FAIL"); + $finish (1); + end + endrule + + // Collect B response; display and check + rule rl_B; + let b <- pop_o (buf_M.ifc_S.o_B); + rg_num_B <= rg_num_B + 1; + + User_struct u_struct = unpack (b.buser); + if (verbosity == 1) begin + $display (" M%0d: ", id, fshow_B (b)); + $display (" M%0d: ", id, fmt_User_struct (u_struct)); + end + + let err <- fav_check_resp ("B", b.bid, b.bresp, b.buser, vf_wr_serial_nums); + + if (err || (verbosity > 1)) begin + $display (" M%0d: ", id, fshow (b)); + $display (" M%0d: ", id, fmt_User_struct (u_struct)); + end + + if (err) begin + $display ("FAIL"); + $finish (1); + end + endrule + + // ---------------------------------------------------------------- + // Stimulus generation completion + + rule rl_stimulus_completed (rg_serial_num == fromInteger (num_xactions + 1)); + $display ("M%0d: COMPLETED stimulus generation", id); + rg_serial_num <= rg_serial_num + 1; + endrule + + // ================================================================ + // INTERFACE + + interface ifc_M = buf_M.ifc_M; + + method completed = (rg_serial_num >= fromInteger (num_xactions)); + + method print_stats; + actionvalue + $display ("M%0d: total requests:%0d", + id, rg_num_AR + rg_num_AW + rg_num_AR_wild + rg_num_AW_wild); + $display (" ARs:%7d AWs:%7d to supported addrs", + rg_num_AR, rg_num_AW); + $display (" ARs:%7d AWs:%7d to wild (unsupported) addrs", + rg_num_AR_wild, rg_num_AW_wild); + $display (" RS:%7d BS:%7d", rg_num_R, rg_num_B); + Bool ok = ((rg_num_AR + rg_num_AW + rg_num_AR_wild + rg_num_AW_wild) + == (rg_num_R + rg_num_B)); + + if (! ok) + $display ("Mismatched number of requests and responses"); + return ok; + endactionvalue + endmethod +endmodule: mkMbox + +// **************************************************************** +// S box connected to an S port of the fabric +// AXI4 response is computed from AXI4 request. +// Reponse's id and user fields are copied from request. +// For AR request, R.data is set to AR.addr+1 (checked by M) + +(* synthesize *) +module mkSbox #(Bit #(4) s_num) + (AXI4_S_IFC #(Wd_Id_S, Wd_Addr, Wd_Data, Wd_User)); + + AXI4_Buffer_IFC #(Wd_Id_S, Wd_Addr, Wd_Data, Wd_User) buf_S <- mkAXI4_Buffer; + + // ================================================================ + // BEHAVIOR + + // Responses for read requests + rule rl_S_AR; + let ar <- pop_o (buf_S.ifc_M.o_AR); + let r = fv_mkR (ar.arid, zeroExtend (ar.araddr + 1), ar.aruser); + buf_S.ifc_M.i_R.enq (r); + + User_struct u_struct = unpack (ar.aruser); + if (verbosity == 1) begin + $display (" S%0d: ", s_num, fshow_AR (ar), + " ", fmt_User_struct (u_struct)); + $display (" S%0d: ", s_num, fshow_R (r)); + end + else if (verbosity > 1) begin + $display (" S%0d: ", s_num, fshow (ar), + " ", fmt_User_struct (u_struct)); + $display (" S%0d: ", s_num, fshow (r)); + end + endrule + + // Responses for write requests + rule rl_S_AW; + let aw <- pop_o (buf_S.ifc_M.o_AW); + let w <- pop_o (buf_S.ifc_M.o_W); + + User_struct awuser_struct = unpack (aw.awuser); + User_struct wuser_struct = unpack (w.wuser); + + Bool ok = True; + + if (aw.awuser != w.wuser) begin + $display ("FAIL"); + $display (" S%0d: AW: Expecting aw.awuser == w.wuser", s_num); + ok = False; + end + + if ((zeroExtend (aw.awaddr) + 1) != w.wdata) begin + $display ("FAIL"); + $display (" S%0d: W: Expecting aw.awaddr + 1 == w.wdata", s_num); + ok = False; + end + + let wr = fv_mkB (aw.awid, aw.awuser); + buf_S.ifc_M.i_B.enq (wr); + + if ((! ok) || (verbosity == 1)) begin + $display (" S%0d: ", s_num, fshow_AW (aw)); + $display (" S%0d: ", fmt_User_struct (awuser_struct)); + $display (" S%0d: ", s_num, fshow_W (w)); + $display (" S%0d: ", fmt_User_struct (awuser_struct)); + end + + if ((!ok ) || (verbosity > 1)) begin + $display (" S%0d: ", s_num, fshow (aw)); + $display (" S%0d: ", fmt_User_struct (awuser_struct)); + $display (" S%0d: ", s_num, fshow (w)); + $display (" S%0d: ", fmt_User_struct (awuser_struct)); + end + + if (! ok) + $finish (1); + endrule + + // ---------------------------------------------------------------- + + return buf_S.ifc_S; +endmodule: mkSbox + +// ================================================================ +// Top-level of this testbench + +(* synthesize *) +module sysTest_AXI4_Fabric (Empty); + // ---------------- + // Ms + + Stim_IFC m0 <- mkMbox (0); + Stim_IFC m1 <- mkMbox (1); + + Vector #(Num_Ms, + AXI4_M_IFC #(Wd_Id_M, Wd_Addr, Wd_Data, Wd_User)) v_Ms = newVector; + v_Ms [0] = m0.ifc_M; + v_Ms [1] = m1.ifc_M; + + // ---------------- + // Ss + + AXI4_S_IFC #(Wd_Id_S, Wd_Addr, Wd_Data, Wd_User) s0 <- mkSbox (0); + AXI4_S_IFC #(Wd_Id_S, Wd_Addr, Wd_Data, Wd_User) s1 <- mkSbox (1); + AXI4_S_IFC #(Wd_Id_S, Wd_Addr, Wd_Data, Wd_User) s2 <- mkSbox (2); + + Vector #(Num_Ss, + AXI4_S_IFC #(Wd_Id_S, Wd_Addr, Wd_Data, Wd_User)) v_Ss = newVector; + v_Ss [0] = s0; + v_Ss [1] = s1; + v_Ss [2] = s2; + + // ---------------- + // AXI4 2x3 crossbar fabric, passing in Ms and Ss + + Empty fabric <- mkAXI4_Fabric (fn_addr_to_S_num, v_Ms, v_Ss); + + // ---------------- + // Linger for 256 cycles after both stimulus Ms have + // finished generating requests, to allow transactions to complete. + + Reg #(Bit #(12)) rg_linger <- mkReg ('1); + + rule rl_quit (m0.completed && m1.completed); + if (rg_linger == '1 - 5) begin + $display ("All Ms: stimulus generation complete."); + $display (" Lingering to allow in-flight transactions to finish."); + end + + else if (rg_linger == 1) begin + let ok0 <- m0.print_stats; + let ok1 <- m1.print_stats; + $display ("%s", ((ok0 && ok1) ? "PASS" :"FAIL")); + end + + else if (rg_linger == 0) begin + $finish (0); + end + rg_linger <= rg_linger - 1; + endrule + +endmodule: sysTest_AXI4_Fabric + +// ================================================================ + +endpackage diff --git a/testing/bsc.contrib/AMBA_Fabrics/AXI4/Test_AXI4_Mem_Model.bsv b/testing/bsc.contrib/AMBA_Fabrics/AXI4/Test_AXI4_Mem_Model.bsv new file mode 100644 index 0000000..63ab864 --- /dev/null +++ b/testing/bsc.contrib/AMBA_Fabrics/AXI4/Test_AXI4_Mem_Model.bsv @@ -0,0 +1,580 @@ +// Copyright (c) 2021-2023 Bluespec, Inc. All Rights Reserved +// Copyright (c) 2024 Rishiyur S. Nikhil. + +// SPDX-License-Identifier: BSD-3-Clause + +package Test_AXI4_Mem_Model; + +// ================================================================ +// Standalone unit tester for AXI4_Mem_Model.bsv + +// The top-level module is: sysTest_AXI4_Mem_Model + +// Instantiates an Mbox (stimulus generator) +// Instantiates a memory module (which is an Sbox) passing in the MBox. + +// The Mbox generates 'num_xactions' read and write requests containing +// random values in: +// axid, axaddr, axuser (both in and out of bounds) +// wdata, wstrb, wuser +// Currently does not do read or write bursts. + +// The Mbox contains a register file for a "reference memory" +// Each request is sent into the AXI4 port, and also performed in the reference memory. + +// For B responses, +// The same write is performed on "reference memory" +// check: bid == awid, buser == awuser +// For R responses: +// The same read is performed on "reference memory" and checked with rdata +// check: rid == arid, ruser == aruser + +// POTENTIAL IMPROVEMENTS: +// * Currently does not generate/test bursts (all transactions are single-beat) + +// ================================================================ +// Bluespec library imports + +import Vector :: *; +import FIFOF :: *; +import RegFile :: *; +import LFSR :: *; + +// ---------------- +// BSV additional libs + +import Semi_FIFOF :: *; +import GetPut_Aux :: *; // for FIFO 'pop' + +// ================================================================ +// Project imports + +import AXI4_Types :: *; +import AXI4_Mem_Model :: *; + +// **************************************************************** +// Number of rd/wr transactions to generate (by each M). +// This number chosen for reasonable runtime in iverilog. + +Integer num_xactions = 10000; + +// ================================================================ +// Simulation verbosity during simulation on stdout for this package (edit as desired) +// 0: quiet +// 1: show xactions brief +// 2: show xactions detail + +Integer verbosity = 0; + +// ================================================================ +// Fabric parameters + +// ================================================================ +// AXI4 parameters + +typedef Bit #(20) Serial_Num; // xaction serial number + +typedef 5 Wd_Id; // M number +typedef 14 Wd_Addr; // to accomodate upto addr_lim +typedef 32 Wd_Data; // == 4 Bytes + +AXI4_Size axsize_full = axsize_4; // full width of data bus + +// 'user' field in AXI4 responses contain this struct +typedef struct { + Bit #(10) user; + Serial_Num serial_num; +} User_struct +deriving (Bits, FShow); + +typedef SizeOf #(User_struct) Wd_User; + +// More compact output than fshow +function Fmt fmt_User_struct (User_struct u); + Fmt f = $format ("User{user:%0h #0x%0h}", u.user, u.serial_num); + return f; +endfunction + +// ================================================================ +// Addresses for the Mem_Model + +Integer addr_base = 'h_2000; // 8KB +Integer addr_lim = 'h_3000; // size 4 KB + +// Memory is implemented with Bit #(Wd_Data) words (4 bytes wide) + +typedef 12 Wd_AddrMW; // width of address of memword + +Integer addrMW_base = addr_base / 4; +Integer addrMW_lim = addr_lim / 4; + +// **************************************************************** +// M box for stimulus generation + +interface Stim_IFC; + interface AXI4_M_IFC #(Wd_Id, Wd_Addr, Wd_Data, Wd_User) ifc_M; + method Bool completed; + method Action print_stats; +endinterface + +// ---------------------------------------------------------------- +// Currently we don't stream requests to memory, because we have no +// mechanism to order reads and writes, which travel on entirely +// separate busses (and thus may be serviced by memory out of order). + +// So, the MBox is a simple state machine + +typedef enum {STATE_INIT_1, + STATE_INIT_2, + STATE_Ax, + STATE_W, + STATE_B, + STATE_R } State +deriving (Bits, Eq, FShow); + +// ---------------------------------------------------------------- +// Help functions to create AXI4 packets + +function AXI4_AW #(Wd_Id, Wd_Addr, Wd_User) fv_mkAW (Bit #(Wd_Id) id, + Bit #(Wd_Addr) addr, + Bit #(Wd_User) user); + + return AXI4_AW {awid: id, + awaddr: addr, + awlen: 0, // 1 beat + awsize: axsize_full, + awburst: axburst_incr, + awlock: axlock_normal, + awcache: awcache_norm_noncache_nonbuf, + awprot: 0, + awqos: 0, + awregion: 0, + awuser: user}; +endfunction + +function AXI4_AR #(Wd_Id, Wd_Addr, Wd_User) fv_mkAR (Bit #(Wd_Id) id, + Bit #(Wd_Addr) addr, + Bit #(Wd_User) user); + + return AXI4_AR {arid: id, + araddr: addr, + arlen: 0, // 1 beat + arsize: axsize_full, + arburst: axburst_incr, + arlock: axlock_normal, + arcache: arcache_norm_noncache_nonbuf, + arprot: 0, + arqos: 0, + arregion: 0, + aruser: user}; +endfunction + +function AXI4_W #(Wd_Data, Wd_User) fv_mkW (Bit #(Wd_Data) data, + Bit #(TDiv #(Wd_Data, 8)) strb, + Bit #(Wd_User) user); + + return AXI4_W {wdata: data, + wstrb: strb, + wlast: True, // Last beat in burst + wuser: user}; +endfunction + +function AXI4_R #(Wd_Id, Wd_Data, Wd_User) fv_mkR (Bit #(Wd_Id) id, + Bit #(Wd_Data) data, + Bit #(Wd_User) user); + return AXI4_R {rid: id, + rdata: data, + rresp: axi4_resp_okay, + rlast: True, // Last beat in burst + ruser: user}; +endfunction + +function AXI4_B #(Wd_Id, Wd_User) fv_mkB (Bit #(Wd_Id) req_id, + Bit #(Wd_User) req_user); + return AXI4_B {bid: req_id, + bresp: axi4_resp_okay, + buser: req_user}; +endfunction + +// ---------------------------------------------------------------- +// M box for stimulus and responses. +// This M box generates 'num_xactions' random AXI4 requests +// and (concurrently) receives and checks the AXI4 responses. + +(* synthesize *) +module mkMbox #(parameter Bit #(4) m_id) (Stim_IFC); + + // Transactor for interface + AXI4_Buffer_IFC #(Wd_Id, Wd_Addr, Wd_Data, Wd_User) buf_M <- mkAXI4_Buffer; + + // ---------------- + // Pseudo-random number generators + LFSR #(Bit #(32)) lfsr_a <- mkLFSR_32; // {1'r/w, 5'id, 10'axuser, 4'xxxx, 12'addr} + + function ActionValue #(Tuple4 #(Bool, Bit #(5), Bit #(10), Bit #(12))) + fn_lfsr_a_fields; + actionvalue + let x32 = lfsr_a.value; lfsr_a.next; + Bool is_read = (x32 [31] == 1'b0); + Bit #(Wd_Id) id = x32 [30:26]; + Bit #(10) user = x32 [25:16]; + Bit #(12) offset = x32 [11:0]; + return tuple4 (is_read, id, user, offset); + endactionvalue + endfunction + + LFSR #(Bit #(32)) lfsr_b <- mkLFSR_32; // wdata + LFSR #(Bit #(32)) lfsr_c <- mkLFSR_32; // {16'xxxx, 10'wuser, 4'wstrb} + + function ActionValue #(Tuple2 #(Bit #(10), Bit #(4))) + fn_lfsr_c_fields; + actionvalue + let x32 = lfsr_a.value; lfsr_a.next; + Bit #(10) user = x32 [13:4]; + Bit #(4) wstrb = x32 [3:0]; + return tuple2 (user, wstrb); + endactionvalue + endfunction + + // ---------------- + // When we gen AW, this queue holds info to generate corresponding W + FIFOF #(Tuple2 #(Bit #(Wd_Data), Bit #(Wd_User))) f_W_info <- mkFIFOF; + + // Transaction serial number + Reg #(Serial_Num) rg_serial_num <- mkReg (0); + + // Statistics + Reg #(Bit #(32)) rg_num_AR <- mkReg (0); // read requests + Reg #(Bit #(32)) rg_num_AW <- mkReg (0); // write requests + Reg #(Bit #(32)) rg_num_AR_wild <- mkReg (0); // read/write reqs to wild addrs + Reg #(Bit #(32)) rg_num_AW_wild <- mkReg (0); // read/write reqs to wild addrs + Reg #(Bit #(32)) rg_num_R <- mkReg (0); // read responses + Reg #(Bit #(32)) rg_num_B <- mkReg (0); // write responses + + // ---------------- + // Reference memory + + RegFile #(Bit #(Wd_AddrMW), + Bit #(Wd_Data)) ref_mem <- mkRegFile (fromInteger (addrMW_base), + fromInteger (addrMW_lim - 1)); + + // ---------------- + + Reg #(State) rg_state <- mkReg (STATE_INIT_1); + + Reg #(AXI4_AW #(Wd_Id, Wd_Addr, Wd_User)) rg_AW <- mkRegU; + Reg #(AXI4_W #(Wd_Data, Wd_User)) rg_W <- mkRegU; + Reg #(AXI4_AR #(Wd_Id, Wd_Addr, Wd_User)) rg_AR <- mkRegU; + + // ================================================================ + + Reg #(Bit #(Wd_AddrMW)) rg_addrMW <- mkReg (fromInteger (addrMW_base)); + + rule rl_init_1 (rg_state == STATE_INIT_1); + lfsr_a.seed (32'h_1111_1111); + lfsr_b.seed (32'h_2222_2222); + lfsr_c.seed (32'h_4444_4444); + rg_state <= STATE_INIT_2; + + $display ("================================"); + $display ("Test_AXI4_Mem_Model: initialization"); + $display (" addr_base:0x%0h addr_lim:0x%0h", addr_base, addr_lim); + $display (" addrMW_base:0x%0h addrMW_lim:0x%0h (word addrs)", + addrMW_base, addrMW_lim); + $display (" AXI4 params: Wd_Id:%0d Wd_Addr:%0d Wd_Data:%0d Wd_User:%0d", + valueOf (Wd_Id), valueOf (Wd_Addr), valueOf (Wd_Data), valueOf (Wd_User)); + $display (" Memory contains %0d words, each wd_data bits (%0d bytes) wide", + addrMW_lim - addrMW_base, valueOf (Wd_Data) / 8); + $display (" Zeroing reference memory"); + $display ("================================"); + endrule + + rule rl_init_2 (rg_state == STATE_INIT_2); + ref_mem.upd (rg_addrMW, 0); + if (rg_addrMW != fromInteger (addrMW_lim - 1)) + rg_addrMW <= rg_addrMW + 1; + else begin + $display ("Test_AXI4_Mem_Model: zero'd reference memory"); + rg_state <= STATE_Ax; + end + endrule + + // ================================================================ + // Request generation + + // Generate read and write requests + rule rl_AR_AW ((rg_state == STATE_Ax) + && (rg_serial_num < fromInteger (num_xactions))); + match {.is_read, .id, .user, .offset} <- fn_lfsr_a_fields; + + Bit #(Wd_Addr) addr = fromInteger (addr_base) + zeroExtend (offset); + + // Compute 'axuser' field + let u_struct = User_struct {user: user, + serial_num: rg_serial_num}; + Bit #(Wd_User) axuser = pack (u_struct); + rg_serial_num <= rg_serial_num + 1; + + if (verbosity != 0) + $display ("----------------"); + + // Read transactions + if (is_read) begin + let ar = fv_mkAR (id, addr, axuser); + buf_M.ifc_S.i_AR.enq (ar); + rg_num_AR <= rg_num_AR + 1; + + // Save for response-checking + rg_AR <= ar; + rg_state <= STATE_R; + + if (verbosity == 1) + $display ("mkMBox%0d: ", m_id, fshow_AR (ar)); + else if (verbosity > 1) + $display ("mkMBox%0d: ", m_id, fshow (ar)); + end + + // Write transactions + else begin + let aw = fv_mkAW (id, addr, axuser); + buf_M.ifc_S.i_AW.enq (aw); + rg_num_AW <= rg_num_AW + 1; + + // Save for W generation and response-checking + rg_AW <= aw; + rg_state <= STATE_W; + + if (verbosity == 1) + $display ("mkMBox%0d: ", m_id, fshow_AW (aw)); + else if (verbosity > 1) + $display ("mkMBox%0d: ", m_id, fshow (aw)); + end + + if (verbosity != 0) + $display ("mkMBox%0d: ", m_id, fmt_User_struct (u_struct)); + endrule + + // ---------------- + // M: write data for write-transaction + + rule rl_W (rg_state == STATE_W); + let aw = rg_AW; + + let wdata = lfsr_b.value; lfsr_b.next; + match {.user, .wstrb} <- fn_lfsr_c_fields; + + // Compute 'wuser' field + let u_struct = User_struct {user: user, + serial_num: unpack (aw.awuser).serial_num}; + Bit #(Wd_User) wuser = pack (u_struct); + + let w = fv_mkW (wdata, wstrb, wuser); + buf_M.ifc_S.i_W.enq (w); + + // Save for response-checking + rg_W <= w; + rg_state <= STATE_B; + + if (verbosity == 1) begin + $display ("mkMBox%0d: ", m_id, fshow_W (w)); + $display ("mkMBox%0d: ", m_id, fmt_User_struct (u_struct)); + end + else if (verbosity > 1) begin + $display ("mkMBox%0d: ", m_id, fshow (w)); + $display ("mkMBox%0d: ", m_id, fmt_User_struct (u_struct)); + end + endrule + + // ---------------------------------------------------------------- + // Stimulus generation completion + + rule rl_stimulus_completed (rg_serial_num == fromInteger (num_xactions + 1)); + $display ("mkMBox%0d: COMPLETED stimulus generation", m_id); + rg_serial_num <= rg_serial_num + 1; + endrule + + // ================================================================ + // Response collection and checking + + // ---------------------------------------------------------------- + // B responses + + rule rl_B (rg_state == STATE_B); + let b <- pop_o (buf_M.ifc_S.o_B); + rg_num_B <= rg_num_B + 1; + + Bool err = False; + if (rg_AW.awid != b.bid) begin + $display ("ERROR: rl_B: awid != bid"); + err = True; + end + + if (rg_AW.awuser != b.buser) begin + $display ("ERROR: rl_B: awuser != buser"); + err = True; + end + + // Perform the same write on reference memory + if ((! err) && (b.bresp == axi4_resp_okay)) begin + Bit #(Wd_AddrMW) addrMW = truncateLSB (rg_AW.awaddr); + let old_d32 = ref_mem.sub (addrMW); + Bit #(Wd_Data) mask = fn_strb_to_bitmask (rg_W.wstrb); + let new_d32 = (old_d32 & (~ mask)) | (rg_W.wdata & mask); + ref_mem.upd (addrMW, new_d32); + end + + // Brief display + if ((! err) && (verbosity == 1)) begin + $display ("Ok: rl_B"); + $display (" ", fshow_AW (rg_AW)); + $display (" ", fshow_W (rg_W)); + $display (" ", fshow_B (b)); + $display (" ", fmt_User_struct (unpack (b.buser))); + end + + // Longer display + if (err || (verbosity > 1)) begin + if (! err) + $display ("Ok: rl_B"); + $display (" ", fshow_AW (rg_AW)); + $display (" ", fmt_User_struct (unpack (rg_AW.awuser))); + $display (" ", fshow_W (rg_W)); + $display (" ", fmt_User_struct (unpack (rg_W.wuser))); + $display (" ", fshow_B (b)); + $display (" ", fmt_User_struct (unpack (b.buser))); + end + + if (err) begin + $display ("FAIL"); + $finish (1); + end + rg_state <= STATE_Ax; + endrule + + // ---------------------------------------------------------------- + // R responses + + rule rl_R (rg_state == STATE_R); + let r <- pop_o (buf_M.ifc_S.o_R); + rg_num_R <= rg_num_R + 1; + + Bool err = False; + if (rg_AR.arid != r.rid) begin + $display ("ERROR: rl_R: arid != rid"); + err = True; + end + + if (rg_AR.aruser != r.ruser) begin + $display ("ERROR: rl_R: aruser != ruser"); + err = True; + end + + // Perform the same read on reference memory + if ((! err) && (r.rresp == axi4_resp_okay)) begin + Bit #(Wd_AddrMW) addrMW = truncateLSB (rg_AR.araddr); + let old_d32 = ref_mem.sub (addrMW); + if (old_d32 != r.rdata) begin + $display ("ERROR: rl_R: rdata != expected value"); + $display (" addrMW: %0h", addrMW); + $display (" ref mem data: %0h", old_d32); + $display (" mem model rdata: %0h", r.rdata); + err = True; + end + end + + // Brief display + if ((! err) && (verbosity == 1)) begin + $display ("Ok: rl_R"); + $display (" ", fshow_AR (rg_AR)); + $display (" ", fshow_R (r)); + $display (" ", fmt_User_struct (unpack (r.ruser))); + end + + // Longer display + if (err || (verbosity > 1)) begin + if (! err) + $display ("Ok: rl_R"); + $display (" ", fshow_AR (rg_AR)); + $display (" ", fmt_User_struct (unpack (rg_AR.aruser))); + $display (" ", fshow_R (r)); + $display (" ", fmt_User_struct (unpack (r.ruser))); + end + + if (err) begin + $display ("FAIL"); + $finish (1); + end + rg_state <= STATE_Ax; + endrule + + // ================================================================ + // INTERFACE + + interface ifc_M = buf_M.ifc_M; + + method completed = (rg_serial_num >= fromInteger (num_xactions)); + + method print_stats; + action + $display ("mkMBox%0d: total requests:%0d", + m_id, rg_num_AR + rg_num_AW + rg_num_AR_wild + rg_num_AW_wild); + $display (" ARs:%7d AWs:%7d to supported addrs", + rg_num_AR, rg_num_AW); + $display (" ARs:%7d AWs:%7d to wild (unsupported) addrs", + rg_num_AR_wild, rg_num_AW_wild); + $display (" RS:%7d BS:%7d", rg_num_R, rg_num_B); + Bool ok = ((rg_num_AR + rg_num_AW + rg_num_AR_wild + rg_num_AW_wild) + == (rg_num_R + rg_num_B)); + + if (ok) + $display ("PASS"); + else begin + $display ("Mismatched number of requests and responses"); + $display ("FAIL"); + end + endaction + endmethod +endmodule: mkMbox + +// **************************************************************** +// Top-level of this testbench + +(* synthesize *) +module sysTest_AXI4_Mem_Model (Empty); + // ---------------- + // Ms + + Stim_IFC m0 <- mkMbox (0); + + Empty _ifc <- mkAXI4_Mem_Model (0, fromInteger (addr_base), fromInteger (addr_lim), + True, // init to zero + False, // init from memhex file + "memhex_filename", + m0.ifc_M); + + // ---------------- + // Linger for 256 cycles after both stimulus Ms have + // finished generating requests, to allow transactions to complete. + + Reg #(Bit #(12)) rg_linger <- mkReg ('1); + + rule rl_quit (m0.completed); + if (rg_linger == '1 - 5) begin + $display ("sysTest_AXI4_Mem_Model: All Ms: stimulus generation complete."); + $display (" Lingering to allow in-flight transactions to finish."); + end + + else if (rg_linger == 1) + let ok0 <- m0.print_stats; + + else if (rg_linger == 0) begin + $finish (0); + end + rg_linger <= rg_linger - 1; + endrule + +endmodule + +// ================================================================ + +endpackage diff --git a/testing/bsc.contrib/AMBA_Fabrics/AXI4/Test_AXI4_to_LDST.bsv b/testing/bsc.contrib/AMBA_Fabrics/AXI4/Test_AXI4_to_LDST.bsv new file mode 100644 index 0000000..22ab1e6 --- /dev/null +++ b/testing/bsc.contrib/AMBA_Fabrics/AXI4/Test_AXI4_to_LDST.bsv @@ -0,0 +1,430 @@ +// Copyright (c) 2021-2023 Bluespec, Inc. All Rights Reserved +// Copyright (c) 2024 Rishiyur S. Nikhil + +// SPDX-License-Identifier: BSD-3-Clause + +package Test_AXI4_to_LDST; + +// **************************************************************** +// Standalone unit tester for AXI4_Deburster.bsv + +// **************************************************************** +// Bluespec library imports + +import FIFOF :: *; +import Connectable :: *; +import Vector :: *; +import StmtFSM :: *; + +// ---------------- +// BSV additional libs + +import Cur_Cycle :: *; +import Semi_FIFOF :: *; + +// ================================================================ +// Local imports + +import AXI4_Types :: *; +import AXI4_to_LDST :: *; + +// **************************************************************** + +Integer verbosity = 0; + +// ================================================================ +// Synthesized instance of module + +typedef 16 Wd_Id; +typedef 64 Wd_Addr; +typedef 512 Wd_AXI_Data; +typedef 0 Wd_User; + +typedef 64 Wd_LDST_Data; + +typedef AXI4_to_LDST_IFC #(Wd_Id, + Wd_Addr, + Wd_AXI_Data, + Wd_User, + Wd_LDST_Data) AXI4_to_LDST_1_IFC; + +/* DELETE +(* synthesize *) +module mkAXI4_to_LDST_1 (AXI4_to_LDST_1_IFC); + let m <- mkAXI4_to_LDST; + return m; +endmodule +*/ + +// ================================================================ + +(* synthesize *) +module sysTest_AXI4_to_LDST (Empty); + + // Transactor representing M + AXI4_Buffer_IFC #(Wd_Id, Wd_Addr, Wd_AXI_Data, Wd_User) + axi4_M_xactor <- mkAXI4_Buffer; + + // The DUT + AXI4_to_LDST_1_IFC dut <- mkAXI4_to_LDST (axi4_M_xactor.ifc_M); + + // This bool controls whether we issue the next AXI request only + // after the prev one has responded, or whether we pipeline them. + Reg #(Bool) rg_one_at_a_time <- mkReg (True); + Reg #(Bool) rg_prev_done <- mkReg (True); + + function Stmt fa_set_one_at_a_time (Bool b); + return seq + delay (32); // To allow quiescence + action + rg_one_at_a_time <= b; + $display ("================================================================"); + if (b) + $display ("%0d: INFO: Setting Mode to: one-at-a-time", cur_cycle); + else + $display ("%0d: INFO: Setting Mode to: pipelined, cur_cycle", cur_cycle); + endaction + endseq; + endfunction + + // ================================================================ + // Templates for AXI structs (we modify these slightly in tests) + + AXI4_AW #(Wd_Id, Wd_Addr, Wd_User) aw0 = AXI4_AW {awid: 0, + awaddr: 'h_8000_0000, + awlen: (1 - 1), + awsize: axsize_8, + awburst: axburst_incr, + awlock: 0, + awcache: 0, + awprot: 0, + awqos: 0, + awregion: 0, + awuser: 0}; + + AXI4_AR #(Wd_Id, Wd_Addr, Wd_User) ar0 = AXI4_AR {arid: 0, + araddr: 'h_8000_0000, + arlen: (1 - 1), + arsize: axsize_8, + arburst: axburst_incr, + arlock: 0, + arcache: 0, + arprot: 0, + arqos: 0, + arregion: 0, + aruser: 0}; + + // 'values' is a vector of bytes [0, 1, 2, ...] + function Bit #(8) fn_for_vector_gen (Integer i); + return fromInteger (i); + endfunction + + Vector #(TDiv #(Wd_AXI_Data, 8), Bit #(8)) values = genWith (fn_for_vector_gen); + + AXI4_W #(Wd_AXI_Data, Wd_User) w0 = AXI4_W {wdata: pack (values), + wstrb: '1, + wlast: True, + wuser: 0}; + + function Action fa_show_axi_data (Bit #(Wd_AXI_Data) axi_data); + action + Vector #(TDiv #(Wd_AXI_Data, Wd_LDST_Data), + Bit #(Wd_LDST_Data)) v_slices = unpack (axi_data); + + Integer slices_per_axi_data_I = valueOf (TDiv #(Wd_AXI_Data, Wd_LDST_Data)); + + for (Integer row = 0; row < 2; row = row + 1) begin + $write (" "); + for (Integer col = 0; col < 4; col = col + 1) + $write (" %016h", v_slices [row * 4 + (3 - col)]); // little-endian + $display (""); + end + endaction + endfunction + + // ================================================================ + // The following rules handle the LD/ST requests emerging from + // the AXI4_to_LDST transformer, returning synthetic responses. + + // ---------------- + // Store request handler + + rule rl_st_handler; + match { .sizecode, .addr, .data } = dut.st_reqs.first; + dut.st_reqs.deq; + + $write ("%0d: rl_st_handler:", cur_cycle); + case (sizecode) + 2'b00: $write (" SB"); + 2'b01: $write (" SH"); + 2'b10: $write (" SW"); + 2'b11: $write (" SD"); + endcase + $display (" addr %0h data %16h", addr, data); + + Bool aligned = ( (sizecode == ldst_b) + || ((sizecode == ldst_h) && (addr [0] == 1'b0)) + || ((sizecode == ldst_w) && (addr [1:0] == 2'b00)) + || ((sizecode == ldst_d) && (addr [2:0] == 3'b000))); + if (! aligned) begin + $display ("%0d: ERROR: rl_st_handler", cur_cycle); + $display (" MISALIGNED sizecode %0h addr %0h data %0h", sizecode, addr, data); + end + + dut.st_rsps.enq (! aligned); + endrule + + // ---------------- + // Load request handler + + rule rl_ld_handler; + match { .sizecode, .addr } = dut.ld_reqs.first; + dut.ld_reqs.deq; + + Bool aligned = ( (sizecode == ldst_b) + || ((sizecode == ldst_h) && (addr [0] == 1'b0)) + || ((sizecode == ldst_w) && (addr [1:0] == 2'b00)) + || ((sizecode == ldst_d) && (addr [2:0] == 3'b000))); + + let shift_amt = ((addr & 'h3F) << 3); + Bit #(Wd_LDST_Data) rdata = truncate (pack (values) >> shift_amt); + case (sizecode) + 2'b00: rdata = (rdata & 'h_FF); + 2'b01: rdata = (rdata & 'h_FFFF); + 2'b10: rdata = (rdata & 'h_FFFF_FFFF); + endcase + + $write ("%0d: rl_ld_handler:", cur_cycle); + case (sizecode) + 2'b00: begin $write (" LB"); rdata = (rdata & 'h_FF); end + 2'b01: begin $write (" LH"); rdata = (rdata & 'h_FFFF); end + 2'b10: begin $write (" LW"); rdata = (rdata & 'h_FFFF_FFFF); end + 2'b11: begin $write (" LD"); end + endcase + $write (" addr %0h", addr); + $display (" => data %0h", rdata); + + if (! aligned) begin + $display ("%0d: ERROR: rl_ld_handler", cur_cycle); + $display (" MISALIGNED sizecode %0h addr %0h", sizecode, addr); + end + + dut.ld_rsps.enq (tuple2 ((! aligned), rdata)); + endrule + + // ================================================================ + // The following rules handle the final AXI responses + + // ---------------- + // AXI Wr_Resp sink + + rule rl_AXI4_B; + let b <- pop_o (axi4_M_xactor.ifc_S.o_B); + rg_prev_done <= True; + + $display ("%0d: rl_AXI4_B: ", cur_cycle, fshow (b)); + + if (b.bid == '1) begin + $display (" Sentinel b (bid == '1); exit"); + $finish (0); + end + endrule + + // ---------------- + // AXI Rd_Data sink + + rule rl_AXI4_R; + let r <- pop_o (axi4_M_xactor.ifc_S.o_R); + rg_prev_done <= True; + + $display ("%0d: rl_AXI4_R: ", cur_cycle); + $display (" rid %0h resp %0h rlast %0h ruser %0h", + r.rid, r.rresp, r.rlast, r.ruser); + fa_show_axi_data (r.rdata); + + if (r.rid == '1) begin + $display (" Sentinel r (rid == '1); exit"); + $finish (0); + end + endrule + + // ================================================================ + // Stimulus (AXI4 requests and responses) + + // ---------------- + + function Action fa_wr_REQ (AXI4_AW #(Wd_Id, Wd_Addr, Wd_User) aw, + AXI4_W #(Wd_AXI_Data, Wd_User) w, + Bit #(16) id, + Bit #(64) addr, + AXI4_Size axsize); + action + await ((! rg_one_at_a_time) || rg_prev_done); + aw.awid = id; + aw.awaddr = addr; + aw.awsize = axsize; + axi4_M_xactor.ifc_S.i_AW.enq (aw); + axi4_M_xactor.ifc_S.i_W.enq (w); + if (rg_one_at_a_time) + rg_prev_done <= False; + + $display ("================"); + $display ("%0d: fa_wr_REQ: id %0h addr %0h sizecode %0h (0x%0h bytes) strb %016h", + cur_cycle, + id, addr, + axsize, fv_AXI4_Size_to_num_bytes (axsize), + w.wstrb); + fa_show_axi_data (w.wdata); + endaction + endfunction + + // ---------------- + + function Action fa_rd_REQ (AXI4_AR #(Wd_Id, Wd_Addr, Wd_User) ar, + Bit #(16) id, + Bit #(64) addr, + AXI4_Size axsize); + action + await ((! rg_one_at_a_time) || rg_prev_done); + ar.arid = id; + ar.araddr = addr; + ar.arsize = axsize; + axi4_M_xactor.ifc_S.i_AR.enq (ar); + if (rg_one_at_a_time) + rg_prev_done <= False; + + $display ("================"); + $display ("%0d: fa_rd_REQ: id %0h addr %0h sizecode %0h (0x%0h bytes)", + cur_cycle, + id, addr, + axsize, fv_AXI4_Size_to_num_bytes (axsize)); + endaction + endfunction + + // ---------------- + // Stimulus tests + + function Stmt test_illegal_wr_req (Bit #(16) id); + return seq + // ---------------- + // - num beats > 1 + // - wlast is True on first beat + // - AWSIZE wider than axi data bus (when axi data bus < widest allowed) + action + let aw1 = aw0; + aw1.awlen = 2; + let wr_data1 = w0; + wr_data1.wlast = True; + Integer num_bytes_axi_data = (valueOf (Wd_AXI_Data) / 8); + let axsize = axsize_32; + if (valueOf (Wd_AXI_Data) < 1024) + axsize = fv_num_bytes_to_AXI4_Size (fromInteger (num_bytes_axi_data * 2)); + fa_wr_REQ (aw1, wr_data1, id, 'h_ffff_ffff, axsize); + endaction + // Send 2 more wr_data since awlen = 2 + axi4_M_xactor.ifc_S.i_W.enq (w0); + axi4_M_xactor.ifc_S.i_W.enq (w0); + endseq; + endfunction + + function Stmt test_illegal_rd_req (Bit #(16) id); + return seq + // ---------------- + // - num beats > 1 + // - AWSIZE wider than axi data bus (when axi data bus < widest allowed) + action + let rd_addr1 = ar0; + rd_addr1.arlen = 2; + Integer num_bytes_axi_data = (valueOf (Wd_AXI_Data) / 8); + let axsize = axsize_32; + if (valueOf (Wd_AXI_Data) < 1024) + axsize = fv_num_bytes_to_AXI4_Size (fromInteger (num_bytes_axi_data * 2)); + fa_rd_REQ (rd_addr1, id, 'h_ffff_ffff, axsize); + endaction + endseq; + endfunction + + function Stmt test_writes (Bool mode, Bit #(16) id0); + return seq + fa_set_one_at_a_time (mode); // True: one-at-a-time, False: Pipelined + + test_illegal_wr_req (id0 + 1); + + // Addr at non-zero bytelane, varying sizes + fa_wr_REQ (aw0, w0, id0 + 2, 'h_8000_0025, axsize_8); + fa_wr_REQ (aw0, w0, id0 + 3, 'h_8000_0025, axsize_16); + fa_wr_REQ (aw0, w0, id0 + 4, 'h_8000_0025, axsize_32); + fa_wr_REQ (aw0, w0, id0 + 5, 'h_8000_0025, axsize_64); + + // Full data: aligned addr, size 64 + fa_wr_REQ (aw0, w0, id0 + 'h10, 'h_4000_0000, axsize_64); + + // Addr at non-zero bytelane, rest of size-window + fa_wr_REQ (aw0, w0, id0 + 'h20, 'h_0001_0009, axsize_8); + + // Addr at non-zero bytelane, rest of size-window, reduced by wstrb + action + let wr_data1 = w0; + wr_data1.wstrb = 'h_0000_0000_0000_7E00; + fa_wr_REQ (aw0, wr_data1, id0 + 'h21, 'h_0001_0009, axsize_8); + endaction + endseq; + endfunction + + function Stmt test_reads (Bool mode, Bit #(16) id0); + return seq + fa_set_one_at_a_time (mode); // True: one-at-a-time, False: Pipelined + + test_illegal_rd_req (id0 + 1); + + // Addr at non-zero bytelane, varying sizes + fa_rd_REQ (ar0, id0 + 2, 'h_8000_0025, axsize_8); + fa_rd_REQ (ar0, id0 + 3, 'h_8000_0025, axsize_16); + fa_rd_REQ (ar0, id0 + 4, 'h_8000_0025, axsize_32); + fa_rd_REQ (ar0, id0 + 5, 'h_8000_0025, axsize_64); + + // Full data: aligned addr, size 64 + fa_rd_REQ (ar0, id0 + 'h10, 'h_4000_0000, axsize_64); + + // Addr at non-zero bytelane, rest of size-window + fa_rd_REQ (ar0, id0 + 'h20, 'h_0001_0009, axsize_8); + endseq; + endfunction + + // ---------------- + // Stimulus FSM + // Set 'if (True/False)' below to do/skip tests. + + FSM fsm_reqs <- mkFSM + (seq + test_writes (True, 'h1000); // one-at-a-time + test_writes (False, 'h2000); // pipelined + + test_reads (True, 'h3000); // one-at-a-time + test_reads (False, 'h4000); // pipelined + + delay (1024); // Drain all pipelines + + // ---------------- + // Sentinel (last) test signalled by arid/awid = '1 + // only one of the follwing is needed + + // fa_wr_REQ (aw0, w0, '1, 'h_ffff_ffff, axsize_8); + fa_rd_REQ (ar0, '1, 'h_ffff_ffff, axsize_8); + endseq); + + // ---------------- + // Start the FSM + + Reg #(Bool) rg_fsm_started <- mkReg (False); + + rule rl_start (! rg_fsm_started); + fsm_reqs.start; + rg_fsm_started <= True; + endrule + +endmodule + +// ================================================================ + +endpackage diff --git a/testing/bsc.contrib/AMBA_Fabrics/AXI4/axi4.exp b/testing/bsc.contrib/AMBA_Fabrics/AXI4/axi4.exp new file mode 100644 index 0000000..3e5d34d --- /dev/null +++ b/testing/bsc.contrib/AMBA_Fabrics/AXI4/axi4.exp @@ -0,0 +1,21 @@ +# Include the boilerplate for bsc-contrib tests +set here [file join [absolute $srcdir] $subdir] +source $here/../../contrib.tcl + +if { $contribtest } { + + add_contrib_dirs_to_path { AMBA_Fabrics/AXI4 AMBA_Fabrics/Utils Misc } + + # Because we use 'genC' to correct the 'cur_cycle', the .ba file + # is specific to the backend and cannot be reused for the other, + # so we use the '_separately' variant of the test procedure + # + test_c_only_bsv_modules_options Test_AXI4_Fabric {} {-aggressive-conditions} + test_veri_only_bsv_modules_options Test_AXI4_Fabric {} {-aggressive-conditions} + test_c_veri_bsv_separately Test_AXI4_Mem_Model + test_c_veri_bsv_separately Test_AXI4_to_LDST + test_c_veri_bsv_separately Test_AXI4_Deburster + + restore_path + +} diff --git a/testing/bsc.contrib/AMBA_Fabrics/AXI4/sysTest_AXI4_Deburster.out.expected b/testing/bsc.contrib/AMBA_Fabrics/AXI4/sysTest_AXI4_Deburster.out.expected new file mode 100644 index 0000000..4f53617 --- /dev/null +++ b/testing/bsc.contrib/AMBA_Fabrics/AXI4/sysTest_AXI4_Deburster.out.expected @@ -0,0 +1,29 @@ +1: M.rl_rd_burst_addr_0 + AXI4_AR { arid: 'h1, araddr: 'h00001000, arlen: 'h01, arsize: 'h3, arburst: 'h1, arlock: 'h0, arcache: 'h0, arprot: 'h0, arqos: 'h0, arregion: 'h0, aruser: 'h001 } +2: M.rl_rd_burst_addr_1 + AXI4_AR { arid: 'h1, araddr: 'h00002000, arlen: 'h03, arsize: 'h3, arburst: 'h1, arlock: 'h0, arcache: 'h0, arprot: 'h0, arqos: 'h0, arregion: 'h0, aruser: 'h001 } +3: S.rl_S_IP_model_AR + AXI4_AR { arid: 'h1, araddr: 'h00001000, arlen: 'h00, arsize: 'h3, arburst: 'h0, arlock: 'h0, arcache: 'h0, arprot: 'h0, arqos: 'h0, arregion: 'h0, aruser: 'h001 } +4: S.rl_S_IP_model_AR + AXI4_AR { arid: 'h1, araddr: 'h00001008, arlen: 'h00, arsize: 'h3, arburst: 'h0, arlock: 'h0, arcache: 'h0, arprot: 'h0, arqos: 'h0, arregion: 'h0, aruser: 'h001 } +5: S.rl_S_IP_model_AR + AXI4_AR { arid: 'h1, araddr: 'h00002000, arlen: 'h00, arsize: 'h3, arburst: 'h0, arlock: 'h0, arcache: 'h0, arprot: 'h0, arqos: 'h0, arregion: 'h0, aruser: 'h001 } +5: M.rl_rd_resps + AXI4_R { rid: 'h1, rdata: 'h0000000000011000, rresp: 'h0, rlast: False, ruser: 'h001 } +6: S.rl_S_IP_model_AR + AXI4_AR { arid: 'h1, araddr: 'h00002008, arlen: 'h00, arsize: 'h3, arburst: 'h0, arlock: 'h0, arcache: 'h0, arprot: 'h0, arqos: 'h0, arregion: 'h0, aruser: 'h001 } +6: M.rl_rd_resps + AXI4_R { rid: 'h1, rdata: 'h0000000000011008, rresp: 'h0, rlast: True, ruser: 'h001 } +7: S.rl_S_IP_model_AR + AXI4_AR { arid: 'h1, araddr: 'h00002010, arlen: 'h00, arsize: 'h3, arburst: 'h0, arlock: 'h0, arcache: 'h0, arprot: 'h0, arqos: 'h0, arregion: 'h0, aruser: 'h001 } +7: M.rl_rd_resps + AXI4_R { rid: 'h1, rdata: 'h0000000000012000, rresp: 'h0, rlast: False, ruser: 'h001 } +8: S.rl_S_IP_model_AR + AXI4_AR { arid: 'h1, araddr: 'h00002018, arlen: 'h00, arsize: 'h3, arburst: 'h0, arlock: 'h0, arcache: 'h0, arprot: 'h0, arqos: 'h0, arregion: 'h0, aruser: 'h001 } +8: M.rl_rd_resps + AXI4_R { rid: 'h1, rdata: 'h0000000000012008, rresp: 'h0, rlast: False, ruser: 'h001 } +9: M.rl_rd_resps + AXI4_R { rid: 'h1, rdata: 'h0000000000012010, rresp: 'h0, rlast: False, ruser: 'h001 } +10: M.rl_rd_resps + AXI4_R { rid: 'h1, rdata: 'h0000000000012018, rresp: 'h0, rlast: True, ruser: 'h001 } +111: rl_idle_quit diff --git a/testing/bsc.contrib/AMBA_Fabrics/AXI4/sysTest_AXI4_Fabric.out.expected b/testing/bsc.contrib/AMBA_Fabrics/AXI4/sysTest_AXI4_Fabric.out.expected new file mode 100644 index 0000000..a331949 --- /dev/null +++ b/testing/bsc.contrib/AMBA_Fabrics/AXI4/sysTest_AXI4_Fabric.out.expected @@ -0,0 +1,13 @@ +M0: COMPLETED stimulus generation +M1: COMPLETED stimulus generation +All Ms: stimulus generation complete. + Lingering to allow in-flight transactions to finish. +M0: total requests:10000 + ARs: 4312 AWs: 4387 to supported addrs + ARs: 661 AWs: 640 to wild (unsupported) addrs + RS: 4973 BS: 5027 +M1: total requests:10000 + ARs: 4305 AWs: 4330 to supported addrs + ARs: 686 AWs: 679 to wild (unsupported) addrs + RS: 4991 BS: 5009 +PASS diff --git a/testing/bsc.contrib/AMBA_Fabrics/AXI4/sysTest_AXI4_Mem_Model.out.expected b/testing/bsc.contrib/AMBA_Fabrics/AXI4/sysTest_AXI4_Mem_Model.out.expected new file mode 100644 index 0000000..3ed36c9 --- /dev/null +++ b/testing/bsc.contrib/AMBA_Fabrics/AXI4/sysTest_AXI4_Mem_Model.out.expected @@ -0,0 +1,25 @@ +================================ +AXI4_Mem_Model[0]: initialization + addr_base: 0x2000 addr_lim: 0x3000 (byte addrs) + addrMW_base:0x800 addrMW_lim:0xc00 (word addrs) + AXI4 params: wd_id:5 wd_addr:14 wd_data:32 wd_user:30 + Memory contains 1024 words, each wd_data bits (4 bytes) wide + Zeroing memory +================================ +================================ +Test_AXI4_Mem_Model: initialization + addr_base:0x2000 addr_lim:0x3000 + addrMW_base:0x800 addrMW_lim:0xc00 (word addrs) + AXI4 params: Wd_Id:5 Wd_Addr:14 Wd_Data:32 Wd_User:30 + Memory contains 1024 words, each wd_data bits (4 bytes) wide + Zeroing reference memory +================================ +AXI4_Mem_Model[0]: zero'd memory +Test_AXI4_Mem_Model: zero'd reference memory +sysTest_AXI4_Mem_Model: All Ms: stimulus generation complete. + Lingering to allow in-flight transactions to finish. +mkMBox0: total requests:10000 + ARs: 4977 AWs: 5023 to supported addrs + ARs: 0 AWs: 0 to wild (unsupported) addrs + RS: 4977 BS: 5023 +PASS diff --git a/testing/bsc.contrib/AMBA_Fabrics/AXI4/sysTest_AXI4_to_LDST.out.expected b/testing/bsc.contrib/AMBA_Fabrics/AXI4/sysTest_AXI4_to_LDST.out.expected new file mode 100644 index 0000000..0ac9545 --- /dev/null +++ b/testing/bsc.contrib/AMBA_Fabrics/AXI4/sysTest_AXI4_to_LDST.out.expected @@ -0,0 +1,320 @@ +================================================================ +34: INFO: Setting Mode to: one-at-a-time +================ +35: fa_wr_REQ: id 1001 addr ffffffff sizecode 7 (0x80 bytes) strb ffffffffffffffff + 1f1e1d1c1b1a1918 1716151413121110 0f0e0d0c0b0a0908 0706050403020100 + 3f3e3d3c3b3a3938 3736353433323130 2f2e2d2c2b2a2928 2726252423222120 +36: AXI4_to_ST:rl_start_xaction ================ + ERROR: illegal AXI4 request + awsize 0x80 bytes > axi data bus width 0x40 bytes +36: AXI4_to_ST:rl_start_xaction ================ + ERROR: illegal AXI4 request + awlen 0x2; only awlen 0 (1-beat bursts) supported + Discarding: AXI4_AW { awid: 'h1001, awaddr: 'h00000000ffffffff, awlen: 'h02, awsize: 'h7, awburst: 'h1, awlock: 'h0, awcache: 'h0, awprot: 'h0, awqos: 'h0, awregion: 'h0, awuser: 'h0 } +41: rl_AXI4_B: AXI4_B { bid: 'h1001, bresp: 'h2, buser: 'h0 } +================ +42: fa_wr_REQ: id 1002 addr 80000025 sizecode 3 (0x8 bytes) strb ffffffffffffffff + 1f1e1d1c1b1a1918 1716151413121110 0f0e0d0c0b0a0908 0706050403020100 + 3f3e3d3c3b3a3938 3736353433323130 2f2e2d2c2b2a2928 2726252423222120 +48: rl_st_handler: SB addr 80000025 data 0000000000000025 +49: rl_st_handler: SH addr 80000026 data 0000000000002726 +52: rl_AXI4_B: AXI4_B { bid: 'h1002, bresp: 'h0, buser: 'h0 } +================ +53: fa_wr_REQ: id 1003 addr 80000025 sizecode 4 (0x10 bytes) strb ffffffffffffffff + 1f1e1d1c1b1a1918 1716151413121110 0f0e0d0c0b0a0908 0706050403020100 + 3f3e3d3c3b3a3938 3736353433323130 2f2e2d2c2b2a2928 2726252423222120 +59: rl_st_handler: SB addr 80000025 data 0000000000000025 +60: rl_st_handler: SH addr 80000026 data 0000000000002726 +61: rl_st_handler: SD addr 80000028 data 2f2e2d2c2b2a2928 +64: rl_AXI4_B: AXI4_B { bid: 'h1003, bresp: 'h0, buser: 'h0 } +================ +65: fa_wr_REQ: id 1004 addr 80000025 sizecode 5 (0x20 bytes) strb ffffffffffffffff + 1f1e1d1c1b1a1918 1716151413121110 0f0e0d0c0b0a0908 0706050403020100 + 3f3e3d3c3b3a3938 3736353433323130 2f2e2d2c2b2a2928 2726252423222120 +71: rl_st_handler: SB addr 80000025 data 0000000000000025 +72: rl_st_handler: SH addr 80000026 data 0000000000002726 +73: rl_st_handler: SD addr 80000028 data 2f2e2d2c2b2a2928 +74: rl_st_handler: SD addr 80000030 data 3736353433323130 +75: rl_st_handler: SD addr 80000038 data 3f3e3d3c3b3a3938 +78: rl_AXI4_B: AXI4_B { bid: 'h1004, bresp: 'h0, buser: 'h0 } +================ +79: fa_wr_REQ: id 1005 addr 80000025 sizecode 6 (0x40 bytes) strb ffffffffffffffff + 1f1e1d1c1b1a1918 1716151413121110 0f0e0d0c0b0a0908 0706050403020100 + 3f3e3d3c3b3a3938 3736353433323130 2f2e2d2c2b2a2928 2726252423222120 +85: rl_st_handler: SB addr 80000025 data 0000000000000025 +86: rl_st_handler: SH addr 80000026 data 0000000000002726 +87: rl_st_handler: SD addr 80000028 data 2f2e2d2c2b2a2928 +88: rl_st_handler: SD addr 80000030 data 3736353433323130 +89: rl_st_handler: SD addr 80000038 data 3f3e3d3c3b3a3938 +92: rl_AXI4_B: AXI4_B { bid: 'h1005, bresp: 'h0, buser: 'h0 } +================ +93: fa_wr_REQ: id 1010 addr 40000000 sizecode 6 (0x40 bytes) strb ffffffffffffffff + 1f1e1d1c1b1a1918 1716151413121110 0f0e0d0c0b0a0908 0706050403020100 + 3f3e3d3c3b3a3938 3736353433323130 2f2e2d2c2b2a2928 2726252423222120 +95: rl_st_handler: SD addr 40000000 data 0706050403020100 +96: rl_st_handler: SD addr 40000008 data 0f0e0d0c0b0a0908 +97: rl_st_handler: SD addr 40000010 data 1716151413121110 +98: rl_st_handler: SD addr 40000018 data 1f1e1d1c1b1a1918 +99: rl_st_handler: SD addr 40000020 data 2726252423222120 +100: rl_st_handler: SD addr 40000028 data 2f2e2d2c2b2a2928 +101: rl_st_handler: SD addr 40000030 data 3736353433323130 +102: rl_st_handler: SD addr 40000038 data 3f3e3d3c3b3a3938 +105: rl_AXI4_B: AXI4_B { bid: 'h1010, bresp: 'h0, buser: 'h0 } +================ +106: fa_wr_REQ: id 1020 addr 10009 sizecode 3 (0x8 bytes) strb ffffffffffffffff + 1f1e1d1c1b1a1918 1716151413121110 0f0e0d0c0b0a0908 0706050403020100 + 3f3e3d3c3b3a3938 3736353433323130 2f2e2d2c2b2a2928 2726252423222120 +109: rl_st_handler: SB addr 10009 data 0000000000000009 +110: rl_st_handler: SH addr 1000a data 0000000000000b0a +111: rl_st_handler: SW addr 1000c data 000000000f0e0d0c +114: rl_AXI4_B: AXI4_B { bid: 'h1020, bresp: 'h0, buser: 'h0 } +================ +115: fa_wr_REQ: id 1021 addr 10009 sizecode 3 (0x8 bytes) strb 0000000000007e00 + 1f1e1d1c1b1a1918 1716151413121110 0f0e0d0c0b0a0908 0706050403020100 + 3f3e3d3c3b3a3938 3736353433323130 2f2e2d2c2b2a2928 2726252423222120 +118: rl_st_handler: SB addr 10009 data 0000000000000009 +119: rl_st_handler: SH addr 1000a data 0000000000000b0a +120: rl_st_handler: SH addr 1000c data 0000000000000d0c +121: rl_st_handler: SB addr 1000e data 000000000000000e +124: rl_AXI4_B: AXI4_B { bid: 'h1021, bresp: 'h0, buser: 'h0 } +================================================================ +148: INFO: Setting Mode to: pipelined, cur_cycle +================ +149: fa_wr_REQ: id 2001 addr ffffffff sizecode 7 (0x80 bytes) strb ffffffffffffffff + 1f1e1d1c1b1a1918 1716151413121110 0f0e0d0c0b0a0908 0706050403020100 + 3f3e3d3c3b3a3938 3736353433323130 2f2e2d2c2b2a2928 2726252423222120 +150: AXI4_to_ST:rl_start_xaction ================ + ERROR: illegal AXI4 request + awsize 0x80 bytes > axi data bus width 0x40 bytes +150: AXI4_to_ST:rl_start_xaction ================ + ERROR: illegal AXI4 request + awlen 0x2; only awlen 0 (1-beat bursts) supported + Discarding: AXI4_AW { awid: 'h2001, awaddr: 'h00000000ffffffff, awlen: 'h02, awsize: 'h7, awburst: 'h1, awlock: 'h0, awcache: 'h0, awprot: 'h0, awqos: 'h0, awregion: 'h0, awuser: 'h0 } +================ +153: fa_wr_REQ: id 2002 addr 80000025 sizecode 3 (0x8 bytes) strb ffffffffffffffff + 1f1e1d1c1b1a1918 1716151413121110 0f0e0d0c0b0a0908 0706050403020100 + 3f3e3d3c3b3a3938 3736353433323130 2f2e2d2c2b2a2928 2726252423222120 +================ +154: fa_wr_REQ: id 2003 addr 80000025 sizecode 4 (0x10 bytes) strb ffffffffffffffff + 1f1e1d1c1b1a1918 1716151413121110 0f0e0d0c0b0a0908 0706050403020100 + 3f3e3d3c3b3a3938 3736353433323130 2f2e2d2c2b2a2928 2726252423222120 +155: rl_AXI4_B: AXI4_B { bid: 'h2001, bresp: 'h2, buser: 'h0 } +159: rl_st_handler: SB addr 80000025 data 0000000000000025 +160: rl_st_handler: SH addr 80000026 data 0000000000002726 +================ +161: fa_wr_REQ: id 2004 addr 80000025 sizecode 5 (0x20 bytes) strb ffffffffffffffff + 1f1e1d1c1b1a1918 1716151413121110 0f0e0d0c0b0a0908 0706050403020100 + 3f3e3d3c3b3a3938 3736353433323130 2f2e2d2c2b2a2928 2726252423222120 +163: rl_AXI4_B: AXI4_B { bid: 'h2002, bresp: 'h0, buser: 'h0 } +166: rl_st_handler: SB addr 80000025 data 0000000000000025 +167: rl_st_handler: SH addr 80000026 data 0000000000002726 +168: rl_st_handler: SD addr 80000028 data 2f2e2d2c2b2a2928 +================ +169: fa_wr_REQ: id 2005 addr 80000025 sizecode 6 (0x40 bytes) strb ffffffffffffffff + 1f1e1d1c1b1a1918 1716151413121110 0f0e0d0c0b0a0908 0706050403020100 + 3f3e3d3c3b3a3938 3736353433323130 2f2e2d2c2b2a2928 2726252423222120 +171: rl_AXI4_B: AXI4_B { bid: 'h2003, bresp: 'h0, buser: 'h0 } +174: rl_st_handler: SB addr 80000025 data 0000000000000025 +175: rl_st_handler: SH addr 80000026 data 0000000000002726 +176: rl_st_handler: SD addr 80000028 data 2f2e2d2c2b2a2928 +177: rl_st_handler: SD addr 80000030 data 3736353433323130 +178: rl_st_handler: SD addr 80000038 data 3f3e3d3c3b3a3938 +================ +179: fa_wr_REQ: id 2010 addr 40000000 sizecode 6 (0x40 bytes) strb ffffffffffffffff + 1f1e1d1c1b1a1918 1716151413121110 0f0e0d0c0b0a0908 0706050403020100 + 3f3e3d3c3b3a3938 3736353433323130 2f2e2d2c2b2a2928 2726252423222120 +181: rl_AXI4_B: AXI4_B { bid: 'h2004, bresp: 'h0, buser: 'h0 } +184: rl_st_handler: SB addr 80000025 data 0000000000000025 +185: rl_st_handler: SH addr 80000026 data 0000000000002726 +186: rl_st_handler: SD addr 80000028 data 2f2e2d2c2b2a2928 +187: rl_st_handler: SD addr 80000030 data 3736353433323130 +188: rl_st_handler: SD addr 80000038 data 3f3e3d3c3b3a3938 +================ +189: fa_wr_REQ: id 2020 addr 10009 sizecode 3 (0x8 bytes) strb ffffffffffffffff + 1f1e1d1c1b1a1918 1716151413121110 0f0e0d0c0b0a0908 0706050403020100 + 3f3e3d3c3b3a3938 3736353433323130 2f2e2d2c2b2a2928 2726252423222120 +190: rl_st_handler: SD addr 40000000 data 0706050403020100 +191: rl_st_handler: SD addr 40000008 data 0f0e0d0c0b0a0908 +191: rl_AXI4_B: AXI4_B { bid: 'h2005, bresp: 'h0, buser: 'h0 } +192: rl_st_handler: SD addr 40000010 data 1716151413121110 +193: rl_st_handler: SD addr 40000018 data 1f1e1d1c1b1a1918 +194: rl_st_handler: SD addr 40000020 data 2726252423222120 +195: rl_st_handler: SD addr 40000028 data 2f2e2d2c2b2a2928 +196: rl_st_handler: SD addr 40000030 data 3736353433323130 +197: rl_st_handler: SD addr 40000038 data 3f3e3d3c3b3a3938 +================ +198: fa_wr_REQ: id 2021 addr 10009 sizecode 3 (0x8 bytes) strb 0000000000007e00 + 1f1e1d1c1b1a1918 1716151413121110 0f0e0d0c0b0a0908 0706050403020100 + 3f3e3d3c3b3a3938 3736353433323130 2f2e2d2c2b2a2928 2726252423222120 +200: rl_st_handler: SB addr 10009 data 0000000000000009 +200: rl_AXI4_B: AXI4_B { bid: 'h2010, bresp: 'h0, buser: 'h0 } +201: rl_st_handler: SH addr 1000a data 0000000000000b0a +202: rl_st_handler: SW addr 1000c data 000000000f0e0d0c +205: rl_st_handler: SB addr 10009 data 0000000000000009 +205: rl_AXI4_B: AXI4_B { bid: 'h2020, bresp: 'h0, buser: 'h0 } +206: rl_st_handler: SH addr 1000a data 0000000000000b0a +207: rl_st_handler: SH addr 1000c data 0000000000000d0c +208: rl_st_handler: SB addr 1000e data 000000000000000e +211: rl_AXI4_B: AXI4_B { bid: 'h2021, bresp: 'h0, buser: 'h0 } +================================================================ +231: INFO: Setting Mode to: one-at-a-time +================ +232: fa_rd_REQ: id 3001 addr ffffffff sizecode 7 (0x80 bytes) +233: AXI4_to_LD:rl_start_xaction ================ + ERROR: illegal AXI4 request + awsize 0x80 bytes > axi data bus width 0x40 bytes +233: AXI4_to_LD:rl_start_xaction ================ + ERROR: illegal AXI4 request + arlen 0x2; only arlen 0 (1-beat bursts) supported + Discarding: AXI4_AR { arid: 'h3001, araddr: 'h00000000ffffffff, arlen: 'h02, arsize: 'h7, arburst: 'h1, arlock: 'h0, arcache: 'h0, arprot: 'h0, arqos: 'h0, arregion: 'h0, aruser: 'h0 } +244: rl_AXI4_R: + rid 3001 resp 2 rlast 1 ruser 0 + 0000000000000000 0000000000000000 0000000000000000 0000000000000000 + 0000000000000000 0000000000000000 0000000000000000 0000000000000000 +================ +245: fa_rd_REQ: id 3002 addr 80000025 sizecode 3 (0x8 bytes) +251: rl_ld_handler: LB addr 80000025 => data 25 +252: rl_ld_handler: LH addr 80000026 => data 2726 +258: rl_AXI4_R: + rid 3002 resp 0 rlast 1 ruser 0 + 0000000000000000 0000000000000000 0000000000000000 0000000000000000 + 0000000000000000 0000000000000000 0000000000000000 2726250000000000 +================ +259: fa_rd_REQ: id 3003 addr 80000025 sizecode 4 (0x10 bytes) +265: rl_ld_handler: LB addr 80000025 => data 25 +266: rl_ld_handler: LH addr 80000026 => data 2726 +267: rl_ld_handler: LD addr 80000028 => data 2f2e2d2c2b2a2928 +272: rl_AXI4_R: + rid 3003 resp 0 rlast 1 ruser 0 + 0000000000000000 0000000000000000 0000000000000000 0000000000000000 + 0000000000000000 0000000000000000 2f2e2d2c2b2a2928 2726250000000000 +================ +273: fa_rd_REQ: id 3004 addr 80000025 sizecode 5 (0x20 bytes) +279: rl_ld_handler: LB addr 80000025 => data 25 +280: rl_ld_handler: LH addr 80000026 => data 2726 +281: rl_ld_handler: LD addr 80000028 => data 2f2e2d2c2b2a2928 +282: rl_ld_handler: LD addr 80000030 => data 3736353433323130 +283: rl_ld_handler: LD addr 80000038 => data 3f3e3d3c3b3a3938 +286: rl_AXI4_R: + rid 3004 resp 0 rlast 1 ruser 0 + 0000000000000000 0000000000000000 0000000000000000 0000000000000000 + 3f3e3d3c3b3a3938 3736353433323130 2f2e2d2c2b2a2928 2726250000000000 +================ +287: fa_rd_REQ: id 3005 addr 80000025 sizecode 6 (0x40 bytes) +293: rl_ld_handler: LB addr 80000025 => data 25 +294: rl_ld_handler: LH addr 80000026 => data 2726 +295: rl_ld_handler: LD addr 80000028 => data 2f2e2d2c2b2a2928 +296: rl_ld_handler: LD addr 80000030 => data 3736353433323130 +297: rl_ld_handler: LD addr 80000038 => data 3f3e3d3c3b3a3938 +300: rl_AXI4_R: + rid 3005 resp 0 rlast 1 ruser 0 + 0000000000000000 0000000000000000 0000000000000000 0000000000000000 + 3f3e3d3c3b3a3938 3736353433323130 2f2e2d2c2b2a2928 2726250000000000 +================ +301: fa_rd_REQ: id 3010 addr 40000000 sizecode 6 (0x40 bytes) +303: rl_ld_handler: LD addr 40000000 => data 706050403020100 +304: rl_ld_handler: LD addr 40000008 => data f0e0d0c0b0a0908 +305: rl_ld_handler: LD addr 40000010 => data 1716151413121110 +306: rl_ld_handler: LD addr 40000018 => data 1f1e1d1c1b1a1918 +307: rl_ld_handler: LD addr 40000020 => data 2726252423222120 +308: rl_ld_handler: LD addr 40000028 => data 2f2e2d2c2b2a2928 +309: rl_ld_handler: LD addr 40000030 => data 3736353433323130 +310: rl_ld_handler: LD addr 40000038 => data 3f3e3d3c3b3a3938 +313: rl_AXI4_R: + rid 3010 resp 0 rlast 1 ruser 0 + 1f1e1d1c1b1a1918 1716151413121110 0f0e0d0c0b0a0908 0706050403020100 + 3f3e3d3c3b3a3938 3736353433323130 2f2e2d2c2b2a2928 2726252423222120 +================ +314: fa_rd_REQ: id 3020 addr 10009 sizecode 3 (0x8 bytes) +317: rl_ld_handler: LB addr 10009 => data 9 +318: rl_ld_handler: LH addr 1000a => data b0a +319: rl_ld_handler: LW addr 1000c => data f0e0d0c +328: rl_AXI4_R: + rid 3020 resp 0 rlast 1 ruser 0 + 0000000000000000 0000000000000000 0f0e0d0c0b0a0900 0000000000000000 + 0000000000000000 0000000000000000 0000000000000000 0000000000000000 +================================================================ +347: INFO: Setting Mode to: pipelined, cur_cycle +================ +348: fa_rd_REQ: id 4001 addr ffffffff sizecode 7 (0x80 bytes) +349: AXI4_to_LD:rl_start_xaction ================ + ERROR: illegal AXI4 request + awsize 0x80 bytes > axi data bus width 0x40 bytes +349: AXI4_to_LD:rl_start_xaction ================ + ERROR: illegal AXI4 request + arlen 0x2; only arlen 0 (1-beat bursts) supported + Discarding: AXI4_AR { arid: 'h4001, araddr: 'h00000000ffffffff, arlen: 'h02, arsize: 'h7, arburst: 'h1, arlock: 'h0, arcache: 'h0, arprot: 'h0, arqos: 'h0, arregion: 'h0, aruser: 'h0 } +================ +349: fa_rd_REQ: id 4002 addr 80000025 sizecode 3 (0x8 bytes) +================ +351: fa_rd_REQ: id 4003 addr 80000025 sizecode 4 (0x10 bytes) +356: rl_ld_handler: LB addr 80000025 => data 25 +357: rl_ld_handler: LH addr 80000026 => data 2726 +================ +358: fa_rd_REQ: id 4004 addr 80000025 sizecode 5 (0x20 bytes) +360: rl_AXI4_R: + rid 4001 resp 2 rlast 1 ruser 0 + 0000000000000000 0000000000000000 0000000000000000 0000000000000000 + 0000000000000000 0000000000000000 0000000000000000 0000000000000000 +365: rl_ld_handler: LB addr 80000025 => data 25 +366: rl_ld_handler: LH addr 80000026 => data 2726 +================ +368: fa_rd_REQ: id 4005 addr 80000025 sizecode 6 (0x40 bytes) +370: rl_AXI4_R: + rid 4002 resp 0 rlast 1 ruser 0 + 0000000000000000 0000000000000000 0000000000000000 0000000000000000 + 0000000000000000 0000000000000000 0000000000000000 2726250000000000 +375: rl_ld_handler: LD addr 80000028 => data 2f2e2d2c2b2a2928 +376: rl_ld_handler: LB addr 80000025 => data 25 +377: rl_ld_handler: LH addr 80000026 => data 2726 +380: rl_AXI4_R: + rid 4003 resp 0 rlast 1 ruser 0 + 0000000000000000 0000000000000000 0000000000000000 0000000000000000 + 0000000000000000 0000000000000000 2f2e2d2c2b2a2928 2726250000000000 +385: rl_ld_handler: LD addr 80000028 => data 2f2e2d2c2b2a2928 +386: rl_ld_handler: LD addr 80000030 => data 3736353433323130 +387: rl_ld_handler: LD addr 80000038 => data 3f3e3d3c3b3a3938 +================ +388: fa_rd_REQ: id 4010 addr 40000000 sizecode 6 (0x40 bytes) +390: rl_AXI4_R: + rid 4004 resp 0 rlast 1 ruser 0 + 0000000000000000 0000000000000000 0000000000000000 0000000000000000 + 3f3e3d3c3b3a3938 3736353433323130 2f2e2d2c2b2a2928 2726250000000000 +393: rl_ld_handler: LB addr 80000025 => data 25 +394: rl_ld_handler: LH addr 80000026 => data 2726 +395: rl_ld_handler: LD addr 80000028 => data 2f2e2d2c2b2a2928 +396: rl_ld_handler: LD addr 80000030 => data 3736353433323130 +397: rl_ld_handler: LD addr 80000038 => data 3f3e3d3c3b3a3938 +================ +398: fa_rd_REQ: id 4020 addr 10009 sizecode 3 (0x8 bytes) +399: rl_ld_handler: LD addr 40000000 => data 706050403020100 +400: rl_ld_handler: LD addr 40000008 => data f0e0d0c0b0a0908 +400: rl_AXI4_R: + rid 4005 resp 0 rlast 1 ruser 0 + 0000000000000000 0000000000000000 0000000000000000 0000000000000000 + 3f3e3d3c3b3a3938 3736353433323130 2f2e2d2c2b2a2928 2726250000000000 +401: rl_ld_handler: LD addr 40000010 => data 1716151413121110 +402: rl_ld_handler: LD addr 40000018 => data 1f1e1d1c1b1a1918 +403: rl_ld_handler: LD addr 40000020 => data 2726252423222120 +404: rl_ld_handler: LD addr 40000028 => data 2f2e2d2c2b2a2928 +405: rl_ld_handler: LD addr 40000030 => data 3736353433323130 +406: rl_ld_handler: LD addr 40000038 => data 3f3e3d3c3b3a3938 +409: rl_ld_handler: LB addr 10009 => data 9 +409: rl_AXI4_R: + rid 4010 resp 0 rlast 1 ruser 0 + 1f1e1d1c1b1a1918 1716151413121110 0f0e0d0c0b0a0908 0706050403020100 + 3f3e3d3c3b3a3938 3736353433323130 2f2e2d2c2b2a2928 2726252423222120 +410: rl_ld_handler: LH addr 1000a => data b0a +411: rl_ld_handler: LW addr 1000c => data f0e0d0c +420: rl_AXI4_R: + rid 4020 resp 0 rlast 1 ruser 0 + 0000000000000000 0000000000000000 0f0e0d0c0b0a0900 0000000000000000 + 0000000000000000 0000000000000000 0000000000000000 0000000000000000 +================ +1423: fa_rd_REQ: id ffff addr ffffffff sizecode 3 (0x8 bytes) +1432: rl_ld_handler: LB addr ffffffff => data 3f +1435: rl_AXI4_R: + rid ffff resp 0 rlast 1 ruser 0 + 0000000000000000 0000000000000000 0000000000000000 0000000000000000 + 3f00000000000000 0000000000000000 0000000000000000 0000000000000000 + Sentinel r (rid == '1); exit diff --git a/testing/bsc.contrib/AMBA_Fabrics/AXI4_Lite/Makefile b/testing/bsc.contrib/AMBA_Fabrics/AXI4_Lite/Makefile new file mode 100644 index 0000000..607fb8d --- /dev/null +++ b/testing/bsc.contrib/AMBA_Fabrics/AXI4_Lite/Makefile @@ -0,0 +1,5 @@ +# for "make clean" to work everywhere + +CONFDIR = $(realpath ../../..) + +include $(CONFDIR)/clean.mk diff --git a/testing/bsc.contrib/AMBA_Fabrics/AXI4_Lite/Test_Fabric.bsv b/testing/bsc.contrib/AMBA_Fabrics/AXI4_Lite/Test_Fabric.bsv new file mode 100644 index 0000000..a6f75c5 --- /dev/null +++ b/testing/bsc.contrib/AMBA_Fabrics/AXI4_Lite/Test_Fabric.bsv @@ -0,0 +1,255 @@ +// Copyright (c) 2019-2024 Bluespec, Inc. All Rights Reserved + +// SPDX-License-Identifier: BSD-3-Clause + +package Test_Fabric; + +// ================================================================ +// Standalone unit tester for AXI4L_Fabric.bsv + +// ================================================================ +// Bluespec library imports + +import FIFOF :: *; +import Connectable :: *; + +// ---------------- +// BSV additional libs + +import Cur_Cycle :: *; + +// ================================================================ +// Project imports + +import Semi_FIFOF :: *; +import AXI4L_Types :: *; +import AXI4L_Fabric :: *; + +// ================================================================ +// Synthesized instance of Fabric + +typedef 2 Num_Ms; +typedef 3 Num_Ss; + +typedef 32 Wd_Addr; +typedef 64 Wd_Data; +typedef 0 Wd_User; + +typedef AXI4L_Fabric_IFC #(Num_Ms, + Num_Ss, + Wd_Addr, + Wd_Data, + Wd_User) AXI4L_Fabric_IFC_Inst; + +function Tuple2 #(Bool, Bit #(TLog #(Num_Ss))) + fn_addr_to_S_num (Bit #(Wd_Addr) addr); + if ((addr & 'h_FFFF_0000) == 0) + return tuple2 (True, 0); + else if ((addr & 'h_FFFF_0000) == 1) + return tuple2 (True, 1); + else if ((addr & 'h_FFFF_0000) == 2) + return tuple2 (True, 2); + else + return tuple2 (False, ?); +endfunction + +(* synthesize *) +module mkAXI4L_Fabric_Inst (AXI4L_Fabric_IFC_Inst); + let m <- mkAXI4L_Fabric (fn_addr_to_S_num); + return m; +endmodule + +AXI4L_M_IFC #(Wd_Addr, Wd_Data, Wd_User) dummy_AXI4L_M_ifc_inst = dummy_AXI4L_M_ifc; +AXI4L_S_IFC #(Wd_Addr, Wd_Data, Wd_User) dummy_AXI4L_S_ifc_inst = dummy_AXI4L_S_ifc; + +// ================================================================ + +(* synthesize *) +module sysTest_Fabric (Empty); + AXI4L_Fabric_IFC_Inst fabric <- mkAXI4L_Fabric_Inst; + + AXI4L_M_Xactor_IFC #(Wd_Addr, Wd_Data, Wd_User) m0 <- mkAXI4L_M_Xactor; + AXI4L_S_Xactor_IFC #(Wd_Addr, Wd_Data, Wd_User) s0 <- mkAXI4L_S_Xactor; + + mkConnection (m0.axi_side, fabric.v_from_Ms [0]); + mkConnection (dummy_AXI4L_M_ifc_inst, fabric.v_from_Ms [1]); + + mkConnection (fabric.v_to_Ss [0], s0.axi_side); + mkConnection (fabric.v_to_Ss [1], dummy_AXI4L_S_ifc_inst); + mkConnection (fabric.v_to_Ss [2], dummy_AXI4L_S_ifc_inst); + + Reg #(Bit #(32)) rg_test <- mkReg (0); // Chooses which test to run + + FIFOF #(Bit #(64)) f_wdata <- mkFIFOF; + + Reg #(Bit #(32)) rg_idle_count <- mkReg (0); + + // ================================================================ + // Help function to create AXI4L channel payloads + + function AXI4L_Wr_Addr #(Wd_Addr, Wd_User) + fv_mk_wr_addr (Bit #(Wd_Addr) addr, Bit #(Wd_User) user); + + return AXI4L_Wr_Addr {awaddr: addr, awprot: 0, awuser: user}; + endfunction + + function AXI4L_Wr_Data #(Wd_Data) + fv_mk_wr_data (Bit #(Wd_Data) data); + + return AXI4L_Wr_Data {wdata: data, wstrb: 'hFF}; + endfunction + + function AXI4L_Wr_Resp #(Wd_User) + fv_mk_wr_resp (AXI4L_Wr_Data #(Wd_Data) wd, Bit #(Wd_User) user); + + return AXI4L_Wr_Resp {bresp: AXI4L_OKAY, buser: user}; + endfunction + + function AXI4L_Rd_Addr #(Wd_Addr, Wd_User) + fv_mk_rd_addr (Bit #(Wd_Addr) addr, Bit #(Wd_User) user); + + return AXI4L_Rd_Addr {araddr: addr, arprot: 0, aruser: user}; + endfunction + + function AXI4L_Rd_Data #(Wd_Data, Wd_User) + fv_mk_rd_data (AXI4L_Rd_Addr #(Wd_Addr, Wd_User) ar); + + return AXI4L_Rd_Data {rresp: AXI4L_OKAY, + rdata: zeroExtend (ar.araddr + 'h10_000), + ruser: ar.aruser}; + endfunction + + // ================================================================ + // STIMULUS + + Bit #(Wd_User) user1 = ((valueOf (Wd_User) == 0) ? 0 : ?); + + rule rl_step0_wra (rg_test == 0); + let wa = fv_mk_wr_addr ('h1000, user1); + m0.i_wr_addr.enq (wa); + + f_wdata.enq (fromInteger ('h1000)); + rg_idle_count <= 0; + rg_test <= 1; + + $display ("%0d: M.rl_step0_wra", cur_cycle); + $display (" ", fshow (wa)); + endrule + + rule rl_step1_wra (rg_test == 1); + let wa = fv_mk_wr_addr ('h1004, user1); + m0.i_wr_addr.enq (wa); + + f_wdata.enq (fromInteger ('h1004)); + rg_idle_count <= 0; + rg_test <= 2; + + $display ("%0d: M.rl_step2_wra", cur_cycle); + $display (" ", fshow (wa)); + endrule + + rule rl_step2_rda (rg_test == 2); + let ra = fv_mk_rd_addr ('h2000, user1); + m0.i_rd_addr.enq (ra); + rg_idle_count <= 0; + rg_test <= 3; + + $display ("%0d: M.rl_step2_rda", cur_cycle); + $display (" ", fshow (ra)); + endrule + + rule rl_step3_rda (rg_test == 3); + let ra = fv_mk_rd_addr ('h2004, user1); + m0.i_rd_addr.enq (ra); + rg_idle_count <= 0; + rg_test <= 4; + + $display ("%0d: M.rl_step3_rda", cur_cycle); + $display (" ", fshow (ra)); + endrule + + rule rl_step4_rda (rg_test == 4); + let ra = fv_mk_rd_addr ('h0003_0000, user1); + m0.i_rd_addr.enq (ra); + rg_idle_count <= 0; + rg_test <= 5; + + $display ("%0d: M.rl_step4_rda", cur_cycle); + $display (" ", fshow (ra)); + endrule + + // ---------------------------------------------------------------- + // M: write data for write-transaction + + rule rl_wr_data; + let data = f_wdata.first; f_wdata.deq; + + let wd = fv_mk_wr_data (data); + m0.i_wr_data.enq (wd); + rg_idle_count <= 0; + + $display ("%0d: M.rl_wr_data", cur_cycle); + $display (" ", fshow (wd)); + endrule + + // ================================================================ + // M: Drain and display responses received by M + + rule rl_wr_resps; + let wr_resp <- pop_o (m0.o_wr_resp); + $display ("%0d: M.rl_wr_resps", cur_cycle); + $display (" ", fshow (wr_resp)); + rg_idle_count <= 0; + endrule + + rule rl_rd_resps; + let rd_resp <- pop_o (m0.o_rd_data); + $display ("%0d: M.rl_rd_resps", cur_cycle); + $display (" ", fshow (rd_resp)); + rg_idle_count <= 0; + endrule + + // ================================================================ + // S: return functional responses + + rule rl_S_IP_model_wr_addr; + let wa <- pop_o (s0.o_wr_addr); + + $display (" %0d: rl_S_IP_model_wr_addr", cur_cycle); + $display (" ", fshow (wa)); + endrule + + rule rl_S_IP_model_wr_data; + let wd <- pop_o (s0.o_wr_data); + s0.i_wr_resp.enq (fv_mk_wr_resp (wd, user1)); + + $display (" %0d: rl_S_IP_model_wr_data", cur_cycle); + $display (" ", fshow (wd)); + endrule + + rule rl_S_IP_model_rd_addr; + let ra <- pop_o (s0.o_rd_addr); + s0.i_rd_data.enq (fv_mk_rd_data (ra)); + + $display (" %0d: rl_S_IP_model_rd_addr", cur_cycle); + $display (" ", fshow (ra)); + endrule + + // ================================================================ + // Watchdog + + rule rl_idle_quit; + if (rg_idle_count == 100) begin + $display ("%0d: rl_idle_quit", cur_cycle); + $finish (0); + end + else begin + rg_idle_count <= rg_idle_count + 1; + end + endrule + +endmodule + +// ================================================================ + +endpackage diff --git a/testing/bsc.contrib/AMBA_Fabrics/AXI4_Lite/axi4_lite.exp b/testing/bsc.contrib/AMBA_Fabrics/AXI4_Lite/axi4_lite.exp new file mode 100644 index 0000000..f5c392b --- /dev/null +++ b/testing/bsc.contrib/AMBA_Fabrics/AXI4_Lite/axi4_lite.exp @@ -0,0 +1,17 @@ +# Include the boilerplate for bsc-contrib tests +set here [file join [absolute $srcdir] $subdir] +source $here/../../contrib.tcl + +if { $contribtest } { + + add_contrib_dirs_to_path { AMBA_Fabrics/AXI4_Lite Misc } + + # Because we use 'genC' to correct the 'cur_cycle', the .ba file + # is specific to the backend and cannot be reused for the other, + # so we use the '_separately' variant of the test procedure + # + test_c_veri_bsv_separately Test_Fabric + + restore_path + +} diff --git a/testing/bsc.contrib/AMBA_Fabrics/AXI4_Lite/sysTest_Fabric.out.expected b/testing/bsc.contrib/AMBA_Fabrics/AXI4_Lite/sysTest_Fabric.out.expected new file mode 100644 index 0000000..2089eed --- /dev/null +++ b/testing/bsc.contrib/AMBA_Fabrics/AXI4_Lite/sysTest_Fabric.out.expected @@ -0,0 +1,37 @@ +1: M.rl_step0_wra + AXI4L_Wr_Addr { awaddr: 'h00001000, awprot: 'h0, awuser: 'h0 } +2: M.rl_step2_wra + AXI4L_Wr_Addr { awaddr: 'h00001004, awprot: 'h0, awuser: 'h0 } +2: M.rl_wr_data + AXI4L_Wr_Data { wdata: 'h0000000000001000, wstrb: 'hff } +3: M.rl_step2_rda + AXI4L_Rd_Addr { araddr: 'h00002000, arprot: 'h0, aruser: 'h0 } +3: M.rl_wr_data + AXI4L_Wr_Data { wdata: 'h0000000000001004, wstrb: 'hff } +4: M.rl_step3_rda + AXI4L_Rd_Addr { araddr: 'h00002004, arprot: 'h0, aruser: 'h0 } +5: M.rl_step4_rda + AXI4L_Rd_Addr { araddr: 'h00030000, arprot: 'h0, aruser: 'h0 } + 6: rl_S_IP_model_wr_addr + AXI4L_Wr_Addr { awaddr: 'h00001000, awprot: 'h0, awuser: 'h0 } + 6: rl_S_IP_model_wr_data + AXI4L_Wr_Data { wdata: 'h0000000000001000, wstrb: 'hff } + 7: rl_S_IP_model_wr_addr + AXI4L_Wr_Addr { awaddr: 'h00001004, awprot: 'h0, awuser: 'h0 } + 7: rl_S_IP_model_wr_data + AXI4L_Wr_Data { wdata: 'h0000000000001004, wstrb: 'hff } + 7: rl_S_IP_model_rd_addr + AXI4L_Rd_Addr { araddr: 'h00002000, arprot: 'h0, aruser: 'h0 } + 8: rl_S_IP_model_rd_addr + AXI4L_Rd_Addr { araddr: 'h00002004, arprot: 'h0, aruser: 'h0 } +10: M.rl_wr_resps + AXI4L_Wr_Resp { bresp: AXI4L_OKAY, buser: 'h0 } +11: M.rl_rd_resps + AXI4L_Rd_Data { rresp: AXI4L_OKAY, rdata: 'h0000000000012000, ruser: 'h0 } +11: M.rl_wr_resps + AXI4L_Wr_Resp { bresp: AXI4L_OKAY, buser: 'h0 } +12: M.rl_rd_resps + AXI4L_Rd_Data { rresp: AXI4L_OKAY, rdata: 'h0000000000012004, ruser: 'h0 } +13: M.rl_rd_resps + AXI4L_Rd_Data { rresp: AXI4L_DECERR, rdata: 'h0000000000000000, ruser: 'h0 } +114: rl_idle_quit diff --git a/testing/bsc.contrib/AMBA_Fabrics/Makefile b/testing/bsc.contrib/AMBA_Fabrics/Makefile new file mode 100644 index 0000000..b953e81 --- /dev/null +++ b/testing/bsc.contrib/AMBA_Fabrics/Makefile @@ -0,0 +1,5 @@ +# for "make clean" to work everywhere + +CONFDIR = $(realpath ../..) + +include $(CONFDIR)/clean.mk