diff options
| author | Martin Braun <martin.braun@ettus.com> | 2020-01-23 16:10:22 -0800 | 
|---|---|---|
| committer | Martin Braun <martin.braun@ettus.com> | 2020-01-28 09:35:36 -0800 | 
| commit | bafa9d95453387814ef25e6b6256ba8db2df612f (patch) | |
| tree | 39ba24b5b67072d354775272e687796bb511848d /fpga/usrp3/lib/rfnoc/crossbar | |
| parent | 3075b981503002df3115d5f1d0b97d2619ba30f2 (diff) | |
| download | uhd-bafa9d95453387814ef25e6b6256ba8db2df612f.tar.gz uhd-bafa9d95453387814ef25e6b6256ba8db2df612f.tar.bz2 uhd-bafa9d95453387814ef25e6b6256ba8db2df612f.zip  | |
Merge FPGA repository back into UHD repository
The FPGA codebase was removed from the UHD repository in 2014 to reduce
the size of the repository. However, over the last half-decade, the
split between the repositories has proven more burdensome than it has
been helpful. By merging the FPGA code back, it will be possible to
create atomic commits that touch both FPGA and UHD codebases. Continuous
integration testing is also simplified by merging the repositories,
because it was previously difficult to automatically derive the correct
UHD branch when testing a feature branch on the FPGA repository.
This commit also updates the license files and paths therein.
We are therefore merging the repositories again. Future development for
FPGA code will happen in the same repository as the UHD host code and
MPM code.
== Original Codebase and Rebasing ==
The original FPGA repository will be hosted for the foreseeable future
at its original local location: https://github.com/EttusResearch/fpga/
It can be used for bisecting, reference, and a more detailed history.
The final commit from said repository to be merged here is
05003794e2da61cabf64dd278c45685a7abad7ec. This commit is tagged as
v4.0.0.0-pre-uhd-merge.
If you have changes in the FPGA repository that you want to rebase onto
the UHD repository, simply run the following commands:
- Create a directory to store patches (this should be an empty
  directory):
    mkdir ~/patches
- Now make sure that your FPGA codebase is based on the same state as
  the code that was merged:
    cd src/fpga # Or wherever your FPGA code is stored
    git rebase v4.0.0.0-pre-uhd-merge
  Note: The rebase command may look slightly different depending on what
  exactly you're trying to rebase.
- Create a patch set for your changes versus v4.0.0.0-pre-uhd-merge:
    git format-patch v4.0.0.0-pre-uhd-merge -o ~/patches
  Note: Make sure that only patches are stored in your output directory.
  It should otherwise be empty. Make sure that you picked the correct
  range of commits, and only commits you wanted to rebase were exported
  as patch files.
- Go to the UHD repository and apply the patches:
    cd src/uhd # Or wherever your UHD repository is stored
    git am --directory fpga ~/patches/*
    rm -rf ~/patches # This is for cleanup
== Contributors ==
The following people have contributed mainly to these files (this list
is not complete):
Co-authored-by: Alex Williams <alex.williams@ni.com>
Co-authored-by: Andrej Rode <andrej.rode@ettus.com>
Co-authored-by: Ashish Chaudhari <ashish@ettus.com>
Co-authored-by: Ben Hilburn <ben.hilburn@ettus.com>
Co-authored-by: Ciro Nishiguchi <ciro.nishiguchi@ni.com>
Co-authored-by: Daniel Jepson <daniel.jepson@ni.com>
Co-authored-by: Derek Kozel <derek.kozel@ettus.com>
Co-authored-by: EJ Kreinar <ej@he360.com>
Co-authored-by: Humberto Jimenez <humberto.jimenez@ni.com>
Co-authored-by: Ian Buckley <ian.buckley@gmail.com>
Co-authored-by: Jörg Hofrichter <joerg.hofrichter@ni.com>
Co-authored-by: Jon Kiser <jon.kiser@ni.com>
Co-authored-by: Josh Blum <josh@joshknows.com>
Co-authored-by: Jonathon Pendlum <jonathan.pendlum@ettus.com>
Co-authored-by: Martin Braun <martin.braun@ettus.com>
Co-authored-by: Matt Ettus <matt@ettus.com>
Co-authored-by: Michael West <michael.west@ettus.com>
Co-authored-by: Moritz Fischer <moritz.fischer@ettus.com>
Co-authored-by: Nick Foster <nick@ettus.com>
Co-authored-by: Nicolas Cuervo <nicolas.cuervo@ettus.com>
Co-authored-by: Paul Butler <paul.butler@ni.com>
Co-authored-by: Paul David <paul.david@ettus.com>
Co-authored-by: Ryan Marlow <ryan.marlow@ettus.com>
Co-authored-by: Sugandha Gupta <sugandha.gupta@ettus.com>
Co-authored-by: Sylvain Munaut <tnt@246tNt.com>
Co-authored-by: Trung Tran <trung.tran@ettus.com>
Co-authored-by: Vidush Vishwanath <vidush.vishwanath@ettus.com>
Co-authored-by: Wade Fife <wade.fife@ettus.com>
Diffstat (limited to 'fpga/usrp3/lib/rfnoc/crossbar')
33 files changed, 5061 insertions, 0 deletions
diff --git a/fpga/usrp3/lib/rfnoc/crossbar/Makefile.srcs b/fpga/usrp3/lib/rfnoc/crossbar/Makefile.srcs new file mode 100644 index 000000000..6fa49cd04 --- /dev/null +++ b/fpga/usrp3/lib/rfnoc/crossbar/Makefile.srcs @@ -0,0 +1,25 @@ +# +# Copyright 2018 Ettus Research, a National Instruments Company +# +# SPDX-License-Identifier: LGPL-3.0-or-later +# + +################################################## +# Crossbar Sources +################################################## +RFNOC_XBAR_SRCS = $(abspath $(addprefix $(BASE_DIR)/../lib/rfnoc/crossbar/, \ +axis_ctrl_crossbar_2d_mesh.v \ +axis_ctrl_crossbar_nxn.v \ +torus_2d_dor_router_single_sw.v \ +mesh_2d_dor_router_single_sw.v \ +axis_ingress_vc_buff.v \ +axis_switch.v \ +axis_port_terminator.v \ +chdr_crossbar_nxn.v \ +chdr_xb_ingress_buff.v \ +chdr_xb_routing_table.v \ +)) + +# Unused sources +# torus_2d_dor_router_multi_sw.v \ +# mesh_2d_dor_router_multi_sw.v \ diff --git a/fpga/usrp3/lib/rfnoc/crossbar/README.pdf b/fpga/usrp3/lib/rfnoc/crossbar/README.pdf Binary files differnew file mode 100644 index 000000000..838702bd1 --- /dev/null +++ b/fpga/usrp3/lib/rfnoc/crossbar/README.pdf diff --git a/fpga/usrp3/lib/rfnoc/crossbar/axis_ctrl_crossbar_2d_mesh.v b/fpga/usrp3/lib/rfnoc/crossbar/axis_ctrl_crossbar_2d_mesh.v new file mode 100644 index 000000000..e69bdfe3c --- /dev/null +++ b/fpga/usrp3/lib/rfnoc/crossbar/axis_ctrl_crossbar_2d_mesh.v @@ -0,0 +1,288 @@ +// +// Copyright 2018 Ettus Research, A National Instruments Company +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// +// Module: axis_ctrl_crossbar_2d_mesh +// Description:  +//   This module implements a 2-dimentional (2d) mesh network (mesh) crossbar +//   for AXIS-CTRL traffic. Supports mesh and torus topologies. +//   It uses AXI-Stream for all of its links.  +//   The torus topology, routing algorithms and the router architecture is  +//   described in README.md in this directory.  +// Parameters: +//   - WIDTH: Width of the AXI-Stream data bus +//   - DIM_SIZE: Number of routers alone one dimension (# Nodes = DIM_SIZE * DIM_SIZE) +//   - TOPOLOGY: Is this a mesh (MESH) or a torus (TORUS) topology +//   - INGRESS_BUFF_SIZE: log2 of the ingress terminal buffer size (in words) +//   - ROUTER_BUFF_SIZE: log2 of the ingress inter-router buffer size (in words) +//   - ROUTING_ALLOC: Algorithm to allocate routing paths between routers. +//     * WORMHOLE: Allocate route as soon as first word in pkt arrives +//     * CUT-THROUGH: Allocate route only after the full pkt arrives +//   - SWITCH_ALLOC: Algorithm to allocate the switch +//     * PRIO: Priority based. Priority: Y-dim > X-dim > Term +//     * ROUND-ROBIN: Round robin input port allocation +//   - DEADLOCK_TIMEOUT: Number of cycles to wait until a deadlock is detected +// Signals: +//   - s_axis_*: Slave port for router (flattened) +//   - m_axis_*: Master port for router (flattened) +// + +module axis_ctrl_crossbar_2d_mesh #( +  parameter DIM_SIZE          = 4, +  parameter WIDTH             = 64, +  parameter TOPOLOGY          = "MESH", +  parameter INGRESS_BUFF_SIZE = 5, +  parameter ROUTER_BUFF_SIZE  = 5, +  parameter ROUTING_ALLOC     = "WORMHOLE", +  parameter SWITCH_ALLOC      = "PRIO", +  parameter DEADLOCK_TIMEOUT  = 16384 +) ( +  input  wire                                 clk, +  input  wire                                 reset, +  // Inputs +  input  wire [(DIM_SIZE*DIM_SIZE*WIDTH)-1:0] s_axis_tdata, +  input  wire [DIM_SIZE*DIM_SIZE-1:0]         s_axis_tlast, +  input  wire [DIM_SIZE*DIM_SIZE-1:0]         s_axis_tvalid, +  output wire [DIM_SIZE*DIM_SIZE-1:0]         s_axis_tready, +  // Output +  output wire [(DIM_SIZE*DIM_SIZE*WIDTH)-1:0] m_axis_tdata, +  output wire [DIM_SIZE*DIM_SIZE-1:0]         m_axis_tlast, +  output wire [DIM_SIZE*DIM_SIZE-1:0]         m_axis_tvalid, +  input  wire [DIM_SIZE*DIM_SIZE-1:0]         m_axis_tready, +  // Deadlock alert +  output wire                                 deadlock_detected +); + +  `include "mesh_node_mapping.vh" + +  //------------------------------------------------------- +  // Unflatten input and output ports +  //------------------------------------------------------- + +  wire [WIDTH-1:0] i_tdata_arr [0:DIM_SIZE-1][0:DIM_SIZE-1]; +  wire             i_tlast_arr [0:DIM_SIZE-1][0:DIM_SIZE-1]; +  wire             i_tvalid_arr[0:DIM_SIZE-1][0:DIM_SIZE-1]; +  wire             i_tready_arr[0:DIM_SIZE-1][0:DIM_SIZE-1]; + +  wire [WIDTH-1:0] o_tdata_arr [0:DIM_SIZE-1][0:DIM_SIZE-1]; +  wire             o_tlast_arr [0:DIM_SIZE-1][0:DIM_SIZE-1]; +  wire             o_tvalid_arr[0:DIM_SIZE-1][0:DIM_SIZE-1]; +  wire             o_tready_arr[0:DIM_SIZE-1][0:DIM_SIZE-1]; + +  wire             clear_routers = deadlock_detected; + +  genvar p,x,y; +  generate +    for (p = 0; p < DIM_SIZE*DIM_SIZE; p=p+1) begin +      assign i_tdata_arr[node_to_ydst(p)][node_to_xdst(p)] = s_axis_tdata[p*WIDTH +: WIDTH]; +      assign i_tlast_arr[node_to_ydst(p)][node_to_xdst(p)] = s_axis_tlast[p]; +      assign i_tvalid_arr[node_to_ydst(p)][node_to_xdst(p)] = s_axis_tvalid[p]; +      assign s_axis_tready[p] = i_tready_arr[node_to_ydst(p)][node_to_xdst(p)] | clear_routers; + +      assign m_axis_tdata[p*WIDTH +: WIDTH] = o_tdata_arr[node_to_ydst(p)][node_to_xdst(p)]; +      assign m_axis_tlast[p] = o_tlast_arr [node_to_ydst(p)][node_to_xdst(p)]; +      assign m_axis_tvalid[p] = o_tvalid_arr[node_to_ydst(p)][node_to_xdst(p)] & ~clear_routers; +      assign o_tready_arr[node_to_ydst(p)][node_to_xdst(p)] = m_axis_tready[p]; +    end +  endgenerate + +  //------------------------------------------------------- +  // Instantiate routers +  //------------------------------------------------------- + +  wire [WIDTH-1:0] e2w_tdata_arr [0:DIM_SIZE-1][0:DIM_SIZE-1]; +  wire             e2w_tdest_arr [0:DIM_SIZE-1][0:DIM_SIZE-1]; +  wire             e2w_tlast_arr [0:DIM_SIZE-1][0:DIM_SIZE-1]; +  wire             e2w_tvalid_arr[0:DIM_SIZE-1][0:DIM_SIZE-1]; +  wire             e2w_tready_arr[0:DIM_SIZE-1][0:DIM_SIZE-1]; + +  wire [WIDTH-1:0] w2e_tdata_arr [0:DIM_SIZE-1][0:DIM_SIZE-1]; +  wire             w2e_tdest_arr [0:DIM_SIZE-1][0:DIM_SIZE-1]; +  wire             w2e_tlast_arr [0:DIM_SIZE-1][0:DIM_SIZE-1]; +  wire             w2e_tvalid_arr[0:DIM_SIZE-1][0:DIM_SIZE-1]; +  wire             w2e_tready_arr[0:DIM_SIZE-1][0:DIM_SIZE-1]; + +  wire [WIDTH-1:0] n2s_tdata_arr [0:DIM_SIZE-1][0:DIM_SIZE-1]; +  wire             n2s_tdest_arr [0:DIM_SIZE-1][0:DIM_SIZE-1]; +  wire             n2s_tlast_arr [0:DIM_SIZE-1][0:DIM_SIZE-1]; +  wire             n2s_tvalid_arr[0:DIM_SIZE-1][0:DIM_SIZE-1]; +  wire             n2s_tready_arr[0:DIM_SIZE-1][0:DIM_SIZE-1]; + +  wire [WIDTH-1:0] s2n_tdata_arr [0:DIM_SIZE-1][0:DIM_SIZE-1]; +  wire             s2n_tdest_arr [0:DIM_SIZE-1][0:DIM_SIZE-1]; +  wire             s2n_tlast_arr [0:DIM_SIZE-1][0:DIM_SIZE-1]; +  wire             s2n_tvalid_arr[0:DIM_SIZE-1][0:DIM_SIZE-1]; +  wire             s2n_tready_arr[0:DIM_SIZE-1][0:DIM_SIZE-1]; + +  localparam              N    = DIM_SIZE; +  localparam              NEND = DIM_SIZE - 1; +  localparam [WIDTH-1:0]  ZERO = {WIDTH{1'b0}}; + +  generate +    for (y = 0; y < DIM_SIZE; y=y+1) begin: ydim +      for (x = 0; x < DIM_SIZE; x=x+1) begin: xdim +        if (TOPOLOGY == "MESH") begin +          mesh_2d_dor_router_single_sw #( +            .WIDTH              (WIDTH), +            .DIM_SIZE           (DIM_SIZE), +            .XB_ADDR_X          (x), +            .XB_ADDR_Y          (y), +            .TERM_BUFF_SIZE     (INGRESS_BUFF_SIZE), +            .XB_BUFF_SIZE       (ROUTER_BUFF_SIZE), +            .ROUTING_ALLOC      (ROUTING_ALLOC), +            .SWITCH_ALLOC       (SWITCH_ALLOC) +          ) rtr_i ( +            // Clock and reset +            .clk                (clk), +            .reset              (reset | clear_routers), +            // Terminals +            .s_axis_ter_tdata   (i_tdata_arr [y][x]), +            .s_axis_ter_tlast   (i_tlast_arr [y][x]), +            .s_axis_ter_tvalid  (i_tvalid_arr[y][x]), +            .s_axis_ter_tready  (i_tready_arr[y][x]), +            .m_axis_ter_tdata   (o_tdata_arr [y][x]), +            .m_axis_ter_tlast   (o_tlast_arr [y][x]), +            .m_axis_ter_tvalid  (o_tvalid_arr[y][x]), +            .m_axis_ter_tready  (o_tready_arr[y][x]), +            // West connections +            .s_axis_wst_tdata   ((x != 0)    ? e2w_tdata_arr [y][x]         : ZERO), +            .s_axis_wst_tdest   ((x != 0)    ? e2w_tdest_arr [y][x]         : 1'b0), +            .s_axis_wst_tlast   ((x != 0)    ? e2w_tlast_arr [y][x]         : 1'b0), +            .s_axis_wst_tvalid  ((x != 0)    ? e2w_tvalid_arr[y][x]         : 1'b0), +            .s_axis_wst_tready  (              e2w_tready_arr[y][x]               ), +            .m_axis_wst_tdata   (              w2e_tdata_arr [y][(x+N-1)%N]       ), +            .m_axis_wst_tdest   (              w2e_tdest_arr [y][(x+N-1)%N]       ), +            .m_axis_wst_tlast   (              w2e_tlast_arr [y][(x+N-1)%N]       ), +            .m_axis_wst_tvalid  (              w2e_tvalid_arr[y][(x+N-1)%N]       ), +            .m_axis_wst_tready  ((x != 0)    ? w2e_tready_arr[y][(x+N-1)%N] : 1'b1), +            // East connections +            .s_axis_est_tdata   ((x != NEND) ? w2e_tdata_arr [y][x]         : ZERO), +            .s_axis_est_tdest   ((x != NEND) ? w2e_tdest_arr [y][x]         : 1'b0), +            .s_axis_est_tlast   ((x != NEND) ? w2e_tlast_arr [y][x]         : 1'b0), +            .s_axis_est_tvalid  ((x != NEND) ? w2e_tvalid_arr[y][x]         : 1'b0), +            .s_axis_est_tready  (              w2e_tready_arr[y][x]               ), +            .m_axis_est_tdata   (              e2w_tdata_arr [y][(x+1)%N]         ), +            .m_axis_est_tdest   (              e2w_tdest_arr [y][(x+1)%N]         ), +            .m_axis_est_tlast   (              e2w_tlast_arr [y][(x+1)%N]         ), +            .m_axis_est_tvalid  (              e2w_tvalid_arr[y][(x+1)%N]         ), +            .m_axis_est_tready  ((x != NEND) ? e2w_tready_arr[y][(x+1)%N]   : 1'b1), +            // North connections +            .s_axis_nor_tdata   ((y != 0)    ? s2n_tdata_arr [y][x]         : ZERO), +            .s_axis_nor_tdest   ((y != 0)    ? s2n_tdest_arr [y][x]         : 1'b0), +            .s_axis_nor_tlast   ((y != 0)    ? s2n_tlast_arr [y][x]         : 1'b0), +            .s_axis_nor_tvalid  ((y != 0)    ? s2n_tvalid_arr[y][x]         : 1'b0), +            .s_axis_nor_tready  (              s2n_tready_arr[y][x]               ), +            .m_axis_nor_tdata   (              n2s_tdata_arr [(y+N-1)%N][x]       ), +            .m_axis_nor_tdest   (              n2s_tdest_arr [(y+N-1)%N][x]       ), +            .m_axis_nor_tlast   (              n2s_tlast_arr [(y+N-1)%N][x]       ), +            .m_axis_nor_tvalid  (              n2s_tvalid_arr[(y+N-1)%N][x]       ), +            .m_axis_nor_tready  ((y != 0)    ? n2s_tready_arr[(y+N-1)%N][x] : 1'b1), +            // South connections +            .s_axis_sou_tdata   ((y != NEND) ? n2s_tdata_arr [y][x]         : ZERO), +            .s_axis_sou_tdest   ((y != NEND) ? n2s_tdest_arr [y][x]         : 1'b0), +            .s_axis_sou_tlast   ((y != NEND) ? n2s_tlast_arr [y][x]         : 1'b0), +            .s_axis_sou_tvalid  ((y != NEND) ? n2s_tvalid_arr[y][x]         : 1'b0), +            .s_axis_sou_tready  (              n2s_tready_arr[y][x]               ), +            .m_axis_sou_tdata   (              s2n_tdata_arr [(y+1)%N][x]         ), +            .m_axis_sou_tdest   (              s2n_tdest_arr [(y+1)%N][x]         ), +            .m_axis_sou_tlast   (              s2n_tlast_arr [(y+1)%N][x]         ), +            .m_axis_sou_tvalid  (              s2n_tvalid_arr[(y+1)%N][x]         ), +            .m_axis_sou_tready  ((y != NEND) ? s2n_tready_arr[(y+1)%N][x]   : 1'b1) +          ); +        end else begin +          torus_2d_dor_router_single_sw #( +            .WIDTH              (WIDTH), +            .DIM_SIZE           (DIM_SIZE), +            .XB_ADDR_X          (x), +            .XB_ADDR_Y          (y), +            .TERM_BUFF_SIZE     (INGRESS_BUFF_SIZE), +            .XB_BUFF_SIZE       (ROUTER_BUFF_SIZE), +            .ROUTING_ALLOC      (ROUTING_ALLOC), +            .SWITCH_ALLOC       (SWITCH_ALLOC) +          ) rtr_i ( +            // Clock and reset +            .clk                (clk), +            .reset              (reset | clear_routers), +            // Terminals +            .s_axis_term_tdata  (i_tdata_arr [y][x]), +            .s_axis_term_tlast  (i_tlast_arr [y][x]), +            .s_axis_term_tvalid (i_tvalid_arr[y][x]), +            .s_axis_term_tready (i_tready_arr[y][x]), +            .m_axis_term_tdata  (o_tdata_arr [y][x]), +            .m_axis_term_tlast  (o_tlast_arr [y][x]), +            .m_axis_term_tvalid (o_tvalid_arr[y][x]), +            .m_axis_term_tready (o_tready_arr[y][x]), +            // X-dim connections +            .s_axis_xdim_tdata  (e2w_tdata_arr [y][x]      ), +            .s_axis_xdim_tdest  (e2w_tdest_arr [y][x]      ), +            .s_axis_xdim_tlast  (e2w_tlast_arr [y][x]      ), +            .s_axis_xdim_tvalid (e2w_tvalid_arr[y][x]      ), +            .s_axis_xdim_tready (e2w_tready_arr[y][x]      ), +            .m_axis_xdim_tdata  (e2w_tdata_arr [y][(x+1)%N]), +            .m_axis_xdim_tdest  (e2w_tdest_arr [y][(x+1)%N]), +            .m_axis_xdim_tlast  (e2w_tlast_arr [y][(x+1)%N]), +            .m_axis_xdim_tvalid (e2w_tvalid_arr[y][(x+1)%N]), +            .m_axis_xdim_tready (e2w_tready_arr[y][(x+1)%N]), +            // Y-dim connections +            .s_axis_ydim_tdata  (s2n_tdata_arr [y][x]      ), +            .s_axis_ydim_tdest  (s2n_tdest_arr [y][x]      ), +            .s_axis_ydim_tlast  (s2n_tlast_arr [y][x]      ), +            .s_axis_ydim_tvalid (s2n_tvalid_arr[y][x]      ), +            .s_axis_ydim_tready (s2n_tready_arr[y][x]      ), +            .m_axis_ydim_tdata  (s2n_tdata_arr [(y+1)%N][x]), +            .m_axis_ydim_tdest  (s2n_tdest_arr [(y+1)%N][x]), +            .m_axis_ydim_tlast  (s2n_tlast_arr [(y+1)%N][x]), +            .m_axis_ydim_tvalid (s2n_tvalid_arr[(y+1)%N][x]), +            .m_axis_ydim_tready (s2n_tready_arr[(y+1)%N][x]) +          ); +        end +      end +    end +  endgenerate + +  //------------------------------------------------------- +  // Deadlock detector +  //------------------------------------------------------- +  // A deadlock is defined on an AXIS bus as an extended period +  // where tvlid=1 but tready=0. If at least one slave port is in +  // this state and none of the master ports are then this router +  // will go into a failsafe deadlock recovery mode. The DEADLOCK_TIMEOUT  +  // parameter defines the duration for which this condition has  +  // to be true. In deadlock recovery mode, all routers are held in reset +  // (thus losing all packets in flights) and all input ports are flushed. +   +  wire m_locked = |(m_axis_tvalid & ~m_axis_tready); +  wire s_locked = |(s_axis_tvalid & ~s_axis_tready); + +  // A counter that tracks the duration for which the router is livelocked +  // If the livelock duration is higher than DEADLOCK_TIMEOUT then it is a +  // deadlock +  reg [$clog2(DEADLOCK_TIMEOUT)-1:0] deadlock_counter = DEADLOCK_TIMEOUT-1; +  always @(posedge clk) begin +    if (reset | ~(s_locked & ~m_locked)) begin +      deadlock_counter <= DEADLOCK_TIMEOUT-1; +    end else if (deadlock_counter != 'd0) begin +      deadlock_counter <= deadlock_counter - 1; +    end +  end + +  // A counter that tracks the deadlock recovery period. If the slave ports +  // have no activity for DEADLOCK_TIMEOUT cycles then the router can +  // successfully come out of the deadlocked state. +  reg [$clog2(DEADLOCK_TIMEOUT)-1:0] deadlock_recover_counter = 'd0; +  always @(posedge clk) begin +    if (reset) begin +      deadlock_recover_counter <= 'd0; +    end else if (deadlock_detected) begin +      if (|s_axis_tvalid) +        deadlock_recover_counter <= DEADLOCK_TIMEOUT-1; +      else +        deadlock_recover_counter <= deadlock_recover_counter - 1; +    end else if (deadlock_counter == 'd0) begin +      deadlock_recover_counter <= DEADLOCK_TIMEOUT-1; +    end +  end +  assign deadlock_detected = (deadlock_recover_counter != 0); + +endmodule diff --git a/fpga/usrp3/lib/rfnoc/crossbar/axis_ctrl_crossbar_nxn.v b/fpga/usrp3/lib/rfnoc/crossbar/axis_ctrl_crossbar_nxn.v new file mode 100644 index 000000000..6de082b4c --- /dev/null +++ b/fpga/usrp3/lib/rfnoc/crossbar/axis_ctrl_crossbar_nxn.v @@ -0,0 +1,130 @@ +// +// Copyright 2018 Ettus Research, A National Instruments Company +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// +// Module: axis_ctrl_crossbar_nxn +// Description:  +//   This module implements a 2-dimentional (2d) mesh network (mesh) crossbar +//   for AXIS-CTRL traffic. Supports mesh and torus topologies. +//   It uses AXI-Stream for all of its links.  +//   The torus topology, routing algorithms and the router architecture is  +//   described in README.md in this directory.  +// Parameters: +//   - WIDTH: Width of the AXI-Stream data bus +//   - NPORTS: Number of ports (maximum 1024) +//   - TOPOLOGY: Is this a mesh (MESH) or a torus (TORUS) topology +//   - INGRESS_BUFF_SIZE: log2 of the ingress terminal buffer size (in words) +//   - ROUTER_BUFF_SIZE: log2 of the ingress inter-router buffer size (in words) +//   - ROUTING_ALLOC: Algorithm to allocate routing paths between routers. +//     * WORMHOLE: Allocate route as soon as first word in pkt arrives +//     * CUT-THROUGH: Allocate route only after the full pkt arrives +//   - SWITCH_ALLOC: Algorithm to allocate the switch +//     * PRIO: Priority based. Priority: Y-dim > X-dim > Term +//     * ROUND-ROBIN: Round robin input port allocation +//   - DEADLOCK_TIMEOUT: Number of cycles to wait until a deadlock is detected +// Signals: +//   - s_axis_*: Slave port for router (flattened) +//   - m_axis_*: Master port for router (flattened) +// + +module axis_ctrl_crossbar_nxn #( +  parameter WIDTH             = 32, +  parameter NPORTS            = 10, +  parameter TOPOLOGY          = "TORUS", +  parameter INGRESS_BUFF_SIZE = 5, +  parameter ROUTER_BUFF_SIZE  = 5, +  parameter ROUTING_ALLOC     = "WORMHOLE", +  parameter SWITCH_ALLOC      = "PRIO", +  parameter DEADLOCK_TIMEOUT  = 16384 +) ( +  input  wire                      clk, +  input  wire                      reset, +  // Inputs +  input  wire [(NPORTS*WIDTH)-1:0] s_axis_tdata, +  input  wire [NPORTS-1:0]         s_axis_tlast, +  input  wire [NPORTS-1:0]         s_axis_tvalid, +  output wire [NPORTS-1:0]         s_axis_tready, +  // Output +  output wire [(NPORTS*WIDTH)-1:0] m_axis_tdata, +  output wire [NPORTS-1:0]         m_axis_tlast, +  output wire [NPORTS-1:0]         m_axis_tvalid, +  input  wire [NPORTS-1:0]         m_axis_tready, +  // Deadlock alert +  output wire                      deadlock_detected +); + +  function integer csqrt_max1024; +    input integer value; +    integer i; +  begin +    csqrt_max1024 = 1; +    for (i = 1; i <= 32; i = i + 1)  // sqrt(1024) = 32 +      csqrt_max1024 = csqrt_max1024 + (i*i < value ? 1 : 0); +  end +  endfunction + +  localparam integer DIM_SIZE = csqrt_max1024(NPORTS); + +  wire [(DIM_SIZE*DIM_SIZE*WIDTH)-1:0] i_tdata,  o_tdata ; +  wire [DIM_SIZE*DIM_SIZE-1:0]         i_tlast,  o_tlast ; +  wire [DIM_SIZE*DIM_SIZE-1:0]         i_tvalid, o_tvalid; +  wire [DIM_SIZE*DIM_SIZE-1:0]         i_tready, o_tready; + +  // axis_ctrl_crossbar_2d_mesh needs to scale up in squares +  // i.e. 4, 9, 16, 25, ... but NPORTS can be any number, so +  // instantiate the next highest square number of ports and +  // terminate the rest. +  axis_ctrl_crossbar_2d_mesh #( +    .WIDTH            (WIDTH), +    .DIM_SIZE         (DIM_SIZE), +    .TOPOLOGY         (TOPOLOGY), +    .INGRESS_BUFF_SIZE(INGRESS_BUFF_SIZE), +    .ROUTER_BUFF_SIZE (ROUTER_BUFF_SIZE), +    .ROUTING_ALLOC    (ROUTING_ALLOC), +    .SWITCH_ALLOC     (SWITCH_ALLOC), +    .DEADLOCK_TIMEOUT (DEADLOCK_TIMEOUT) +  ) router_dut_i ( +    .clk              (clk), +    .reset            (reset), +    .s_axis_tdata     (i_tdata), +    .s_axis_tlast     (i_tlast), +    .s_axis_tvalid    (i_tvalid), +    .s_axis_tready    (i_tready), +    .m_axis_tdata     (o_tdata),  +    .m_axis_tlast     (o_tlast), +    .m_axis_tvalid    (o_tvalid),  +    .m_axis_tready    (o_tready), +    .deadlock_detected(deadlock_detected) +  ); + +  // Connect the bottom NPORTS to the IO +  assign i_tdata[(NPORTS*WIDTH)-1:0] = s_axis_tdata; +  assign i_tlast[NPORTS-1:0]         = s_axis_tlast; +  assign i_tvalid[NPORTS-1:0]        = s_axis_tvalid; +  assign s_axis_tready               = i_tready[NPORTS-1:0]; + +  assign m_axis_tdata                = o_tdata[(NPORTS*WIDTH)-1:0]; +  assign m_axis_tlast                = o_tlast[NPORTS-1:0]; +  assign m_axis_tvalid               = o_tvalid[NPORTS-1:0]; +  assign o_tready[NPORTS-1:0]        = m_axis_tready; + +  // Terminate the rest +  genvar i; +  generate for (i = NPORTS; i < (DIM_SIZE*DIM_SIZE); i = i + 1) begin: ports +    axis_port_terminator #(.DATA_W(WIDTH)) term_i ( +      .clk          (clk), +      .reset        (reset), +      .s_axis_tdata (o_tdata[(i*WIDTH)+:WIDTH]), +      .s_axis_tlast (o_tlast[i]), +      .s_axis_tvalid(o_tvalid[i]), +      .s_axis_tready(o_tready[i]), +      .m_axis_tdata (i_tdata[(i*WIDTH)+:WIDTH]), +      .m_axis_tlast (i_tlast[i]), +      .m_axis_tvalid(i_tvalid[i]), +      .m_axis_tready(i_tready[i]), +      .pkts_dropped () +    ); +  end endgenerate + +endmodule diff --git a/fpga/usrp3/lib/rfnoc/crossbar/axis_ingress_vc_buff.v b/fpga/usrp3/lib/rfnoc/crossbar/axis_ingress_vc_buff.v new file mode 100644 index 000000000..fd10d6682 --- /dev/null +++ b/fpga/usrp3/lib/rfnoc/crossbar/axis_ingress_vc_buff.v @@ -0,0 +1,178 @@ +// +// Copyright 2018 Ettus Research, A National Instruments Company +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// +// Module: axis_ingress_vc_buff +// Description: +//  A wrapper around a buffer to implement one or more virtual channels +//  Supports gate a packet for cut-through routing + +module axis_ingress_vc_buff #( +  parameter WIDTH     = 64,         // Width of the datapath +  parameter NUM_VCS   = 2,          // Number of virtual channels +  parameter SIZE      = 5,          // Virtual channel buffer size +  parameter ROUTING   = "WORMHOLE", // Routing (switching) method {WORMHOLE, CUT-THROUGH} +  parameter DEST_W    = (NUM_VCS > 1) ? $clog2(NUM_VCS) : 1 // PRIVATE +) ( +  input  wire               clk, +  input  wire               reset, +  input  wire [WIDTH-1:0]   s_axis_tdata, +  input  wire [DEST_W-1:0]  s_axis_tdest, +  input  wire               s_axis_tlast, +  input  wire               s_axis_tvalid, +  output wire               s_axis_tready, +  output wire [WIDTH-1:0]   m_axis_tdata, +  output wire               m_axis_tlast, +  output wire               m_axis_tvalid, +  input  wire               m_axis_tready +); + +  generate if (NUM_VCS > 1) begin +    //---------------------------------------------------- +    // Multiple virtual channels +    //---------------------------------------------------- + +    wire [(WIDTH*NUM_VCS)-1:0] bufin_tdata , bufout_tdata ; +    wire [NUM_VCS-1:0]         bufin_tlast , bufout_tlast ; +    wire [NUM_VCS-1:0]         bufin_tvalid, bufout_tvalid; +    wire [NUM_VCS-1:0]         bufin_tready, bufout_tready; +   +    axi_demux #( +      .WIDTH(WIDTH), .SIZE(NUM_VCS), +      .PRE_FIFO_SIZE(0 /* must be 0 */), .POST_FIFO_SIZE(0) +    ) vc_demux_i ( +      .clk      (clk), +      .reset    (reset), +      .clear    (1'b0), +      .header   (/* unused */), +      .dest     (s_axis_tdest ), +      .i_tdata  (s_axis_tdata ), +      .i_tlast  (s_axis_tlast ), +      .i_tvalid (s_axis_tvalid), +      .i_tready (s_axis_tready), +      .o_tdata  (bufin_tdata), +      .o_tlast  (bufin_tlast), +      .o_tvalid (bufin_tvalid), +      .o_tready (bufin_tready) +    ); +   +    genvar vc; +    for (vc = 0; vc < NUM_VCS; vc = vc + 1) begin +      if (ROUTING == "WORMHOLE") begin +        axi_fifo #( +          .WIDTH(WIDTH+1), .SIZE(SIZE) +        ) buf_i ( +          .clk      (clk),  +          .reset    (reset),  +          .clear    (1'b0), +          .i_tdata  ({bufin_tlast[vc], bufin_tdata  [(vc*WIDTH)+:WIDTH]}), +          .i_tvalid (bufin_tvalid [vc]), +          .i_tready (bufin_tready [vc]), +          .o_tdata  ({bufout_tlast[vc], bufout_tdata [(vc*WIDTH)+:WIDTH]}), +          .o_tvalid (bufout_tvalid[vc]), +          .o_tready (bufout_tready[vc]), +          .space    (), +          .occupied () +        ); +      end else begin +        axi_packet_gate #( +          .WIDTH(WIDTH), .SIZE(SIZE) +        ) buf_i ( +          .clk      (clk),  +          .reset    (reset),  +          .clear    (1'b0), +          .i_tdata  (bufin_tdata[(vc*WIDTH)+:WIDTH]), +          .i_tlast  (bufin_tlast[vc]), +          .i_tvalid (bufin_tvalid[vc]), +          .i_tready (bufin_tready[vc]), +          .i_terror (1'b0), +          .o_tdata  (bufout_tdata[(vc*WIDTH)+:WIDTH]), +          .o_tlast  (bufout_tlast[vc]), +          .o_tvalid (bufout_tvalid[vc]), +          .o_tready (bufout_tready[vc]) +        ); +      end +    end +   +    axi_mux #( +      .WIDTH(WIDTH), .SIZE(NUM_VCS), +      .PRE_FIFO_SIZE(0), .POST_FIFO_SIZE(1) +    ) vc_mux_i ( +      .clk      (clk), +      .reset    (reset), +      .clear    (1'b0), +      .i_tdata  (bufout_tdata ), +      .i_tlast  (bufout_tlast ), +      .i_tvalid (bufout_tvalid), +      .i_tready (bufout_tready), +      .o_tdata  (m_axis_tdata ), +      .o_tlast  (m_axis_tlast ), +      .o_tvalid (m_axis_tvalid), +      .o_tready (m_axis_tready) +    ); + +  end else begin +    //---------------------------------------------------- +    // Single virtual channel +    //---------------------------------------------------- +    wire [WIDTH-1:0] pipe_tdata; +    wire             pipe_tlast; +    wire             pipe_tvalid; +    wire             pipe_tready; + +    if (ROUTING == "WORMHOLE") begin +      axi_fifo #( +        .WIDTH(WIDTH+1), .SIZE(SIZE) +      ) buf_i ( +        .clk      (clk),  +        .reset    (reset),  +        .clear    (1'b0), +        .i_tdata  ({s_axis_tlast, s_axis_tdata}), +        .i_tvalid (s_axis_tvalid ), +        .i_tready (s_axis_tready ), +        .o_tdata  ({pipe_tlast, pipe_tdata}), +        .o_tvalid (pipe_tvalid), +        .o_tready (pipe_tready), +        .space    (), +        .occupied () +      ); +    end else begin +      axi_packet_gate #( +        .WIDTH(WIDTH), .SIZE(SIZE) +      ) buf_i ( +        .clk      (clk),  +        .reset    (reset),  +        .clear    (1'b0), +        .i_tdata  (s_axis_tdata), +        .i_tlast  (s_axis_tlast), +        .i_tvalid (s_axis_tvalid), +        .i_tready (s_axis_tready), +        .i_terror (1'b0), +        .o_tdata  (pipe_tdata), +        .o_tlast  (pipe_tlast), +        .o_tvalid (pipe_tvalid), +        .o_tready (pipe_tready) +      ); +    end + +    axi_fifo #( +      .WIDTH(WIDTH+1), .SIZE(1) +    ) buf_i ( +      .clk      (clk),  +      .reset    (reset),  +      .clear    (1'b0), +      .i_tdata  ({pipe_tlast, pipe_tdata}), +      .i_tvalid (pipe_tvalid ), +      .i_tready (pipe_tready ), +      .o_tdata  ({m_axis_tlast, m_axis_tdata}), +      .o_tvalid (m_axis_tvalid), +      .o_tready (m_axis_tready), +      .space    (), +      .occupied () +    ); + +  end endgenerate + +endmodule + diff --git a/fpga/usrp3/lib/rfnoc/crossbar/axis_port_terminator.v b/fpga/usrp3/lib/rfnoc/crossbar/axis_port_terminator.v new file mode 100644 index 000000000..bf9fa24be --- /dev/null +++ b/fpga/usrp3/lib/rfnoc/crossbar/axis_port_terminator.v @@ -0,0 +1,44 @@ +// +// Copyright 2018 Ettus Research, A National Instruments Company +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// +// Module: axis_port_terminator +// Description: +//   A dummy terminator for unused crossbar ports + +module axis_port_terminator #( +  parameter DATA_W = 64 +) ( +  // Clocks and resets +  input  wire               clk, +  input  wire               reset, +  // Input ports +  input  wire [DATA_W-1:0]  s_axis_tdata,   // Input data +  input  wire               s_axis_tlast,   // Input EOP (last) +  input  wire               s_axis_tvalid,  // Input valid +  output wire               s_axis_tready,  // Input ready +  // Output ports +  output wire [DATA_W-1:0]  m_axis_tdata,   // Output data +  output wire               m_axis_tlast,   // Output EOP (last)  +  output wire               m_axis_tvalid,  // Output valid +  input  wire               m_axis_tready,  // Output ready +  // Metrics +  output reg  [15:0]        pkts_dropped +); + +  assign s_axis_tready  = 1'b1; +  assign m_axis_tdata   = {DATA_W{1'b0}}; +  assign m_axis_tlast   = 1'b0; +  assign m_axis_tvalid  = 1'b0; + +  always @(posedge clk) begin +    if (reset) begin +      pkts_dropped <= 'd0; +    end else if (s_axis_tvalid & s_axis_tlast & s_axis_tready) begin +      pkts_dropped <= pkts_dropped + 'd1; +    end +  end + +endmodule + diff --git a/fpga/usrp3/lib/rfnoc/crossbar/axis_switch.v b/fpga/usrp3/lib/rfnoc/crossbar/axis_switch.v new file mode 100644 index 000000000..24b9e4129 --- /dev/null +++ b/fpga/usrp3/lib/rfnoc/crossbar/axis_switch.v @@ -0,0 +1,164 @@ +// +// Copyright 2018 Ettus Research, A National Instruments Company +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// +// Module: axis_switch +// Description: +//   Implementation of a M-input, N-output AXI-Stream switch. +//   One of the M input ports is allocated based on the s_axis_alloc signal +//   and the packet on that port is sent to one of the N output ports based +//   on the tdest signal + +module axis_switch #( +  parameter DATA_W     = 64,    // tdata width +  parameter DEST_W     = 1,     // Output tdest width +  parameter IN_PORTS   = 3,     // Number of input ports +  parameter OUT_PORTS  = 3,     // Number of output ports +  parameter PIPELINE   = 1,     // Instantiate output pipeline stage? +  parameter ALLOC_W    = (IN_PORTS == 1) ? 1 : $clog2(IN_PORTS) //PRIVATE +) ( +  // Clocks and resets +  input  wire                                             clk,            // Switch clock +  input  wire                                             reset,          // Reset +  // Input ports +  input  wire [(DATA_W*IN_PORTS)-1:0]                     s_axis_tdata,   // Input data +  input  wire [((DEST_W+$clog2(OUT_PORTS))*IN_PORTS)-1:0] s_axis_tdest,   // Input destination +  input  wire [IN_PORTS-1:0]                              s_axis_tlast,   // Input EOP (last) +  input  wire [IN_PORTS-1:0]                              s_axis_tvalid,  // Input valid +  output wire [IN_PORTS-1:0]                              s_axis_tready,  // Input ready +  input  wire [ALLOC_W-1:0]                               s_axis_alloc,   // Input port allocation for switch +  // Output ports +  output wire [(DATA_W*OUT_PORTS)-1:0]                    m_axis_tdata,   // Output data        +  output wire [(DEST_W*OUT_PORTS)-1:0]                    m_axis_tdest,   // Output destination +  output wire [OUT_PORTS-1:0]                             m_axis_tlast,   // Output EOP (last)  +  output wire [OUT_PORTS-1:0]                             m_axis_tvalid,  // Output valid       +  input  wire [OUT_PORTS-1:0]                             m_axis_tready   // Output ready       +); +  // PRIVATE: Vivado synthesizer workaround (cannot be localparam) +  localparam CLOG2_IN_PORTS = $clog2(IN_PORTS); +  localparam CLOG2_OUT_PORTS = $clog2(OUT_PORTS); + +  //--------------------------------------------------------- +  // Flatten/unflatten and pipeline +  //--------------------------------------------------------- +  wire [DATA_W-1:0]                   i_tdata [0:IN_PORTS-1]; +  wire [DEST_W+$clog2(OUT_PORTS)-1:0] i_tdest [0:IN_PORTS-1]; +  wire                                i_tlast [0:IN_PORTS-1]; +  wire [IN_PORTS-1:0]                 i_tvalid; +  wire [IN_PORTS-1:0]                 i_tready; +  wire [ALLOC_W-1:0]                  i_alloc; +  wire [DATA_W-1:0]                   o_tdata [0:OUT_PORTS-1]; +  wire [DEST_W-1:0]                   o_tdest [0:OUT_PORTS-1]; +  wire                                o_tlast [0:OUT_PORTS-1]; +  wire [OUT_PORTS-1:0]                o_tvalid; +  wire [OUT_PORTS-1:0]                o_tready; + +  genvar i, o; +  generate +    for (i = 0; i < IN_PORTS; i = i + 1) begin: in_ports +      assign i_tdata      [i] = s_axis_tdata [(i*DATA_W)+:DATA_W]; +      assign i_tdest      [i] = s_axis_tdest [(i*(DEST_W+CLOG2_OUT_PORTS))+:(DEST_W+CLOG2_OUT_PORTS)]; +      assign i_tlast      [i] = s_axis_tlast [i]; +      assign i_tvalid     [i] = s_axis_tvalid[i]; +      assign s_axis_tready[i] = i_tready     [i]; +    end +    assign i_alloc = s_axis_alloc;   //i_alloc has to be delay matched to valid + +    for (o = 0; o < OUT_PORTS; o = o + 1) begin +      if (PIPELINE == 1) begin +        axi_fifo_flop2 #(.WIDTH(DEST_W+1+DATA_W)) out_pipe_i ( +          .clk(clk), .reset(reset), .clear(1'b0), +          .i_tdata({o_tdest[o], o_tlast[o], o_tdata[o]}), +          .i_tvalid(o_tvalid[o]), .i_tready(o_tready[o]), +          .o_tdata({m_axis_tdest[(o*DEST_W)+:DEST_W], m_axis_tlast[o], m_axis_tdata[(o*DATA_W)+:DATA_W]}), +          .o_tvalid(m_axis_tvalid[o]), .o_tready(m_axis_tready[o]), +          .space(), .occupied() +        ); +      end else begin +        assign m_axis_tdata [(o*DATA_W)+:DATA_W] = o_tdata      [o]; +        assign m_axis_tdest [(o*DEST_W)+:DEST_W] = o_tdest      [o]; +        assign m_axis_tlast [o]                  = o_tlast      [o]; +        assign m_axis_tvalid[o]                  = o_tvalid     [o]; +        assign o_tready     [o]                  = m_axis_tready[o]; +      end +    end +  endgenerate + +  //--------------------------------------------------------- +  // Allocator +  //--------------------------------------------------------- +  // The "chosen" input port will drive this bus +  wire [DATA_W-1:0]                   master_tdata; +  wire [DEST_W+$clog2(OUT_PORTS)-1:0] master_tdest; +  wire                                master_tlast; +  wire                                master_tvalid; +  wire                                master_tready; + +  generate if (IN_PORTS > 1) begin +    reg [IN_PORTS-1:0] ialloc_oh; +    reg [$clog2(IN_PORTS)-1:0] alloc_reg; +    always @(posedge clk) begin +      if (reset) begin +        ialloc_oh <= {IN_PORTS{1'b0}}; +      end else begin +        if (ialloc_oh == {IN_PORTS{1'b0}}) begin +          if (|i_tvalid) begin +            ialloc_oh[i_alloc] <= 1'b1; +            alloc_reg <= i_alloc; +          end +        end else begin +          if(master_tready & master_tvalid & master_tlast) +            ialloc_oh <= {IN_PORTS{1'b0}}; +        end +      end +    end + +    assign master_tdata   = i_tdata[alloc_reg]; +    assign master_tdest   = i_tdest[alloc_reg]; +    assign master_tlast   = i_tlast[alloc_reg]; +    assign master_tvalid  = |(i_tvalid & ialloc_oh); +    assign i_tready       = i_tvalid & ialloc_oh & {IN_PORTS{master_tready}}; +  end else begin +    // Special case: One input port +    assign master_tdata   = i_tdata[0]; +    assign master_tdest   = i_tdest[0]; +    assign master_tlast   = i_tlast[0]; +    assign master_tvalid  = i_tvalid[0]; +    assign i_tready[0]    = master_tready; +  end endgenerate + +  //--------------------------------------------------------- +  // Router +  //--------------------------------------------------------- +  generate if (OUT_PORTS > 1) begin +    reg [OUT_PORTS-1:0] odst_oh; +    always @(posedge clk) begin +      if (reset) begin +        odst_oh <= {OUT_PORTS{1'b0}}; +      end else begin +        if (odst_oh == {OUT_PORTS{1'b0}}) begin +          if (master_tvalid) +            odst_oh[master_tdest[CLOG2_OUT_PORTS-1:0]] <= 1'b1; +        end else begin +          if(master_tready & master_tvalid & master_tlast) +            odst_oh <= {OUT_PORTS{1'b0}}; +        end +      end +    end +    assign master_tready = |(o_tready & odst_oh); +    assign o_tvalid = {OUT_PORTS{master_tvalid}} & odst_oh; +  end else begin +    // Special case: One output port +    assign master_tready = o_tready[0]; +    assign o_tvalid[0] = master_tvalid; +  end endgenerate + +  generate for (o = 0; o < OUT_PORTS; o = o + 1) begin +    assign o_tdata[o] = master_tdata; +    assign o_tdest[o] = master_tdest[DEST_W+CLOG2_OUT_PORTS-1:CLOG2_OUT_PORTS]; +    assign o_tlast[o] = master_tlast; +  end endgenerate + +endmodule + diff --git a/fpga/usrp3/lib/rfnoc/crossbar/chdr_crossbar_nxn.v b/fpga/usrp3/lib/rfnoc/crossbar/chdr_crossbar_nxn.v new file mode 100644 index 000000000..79f1a6626 --- /dev/null +++ b/fpga/usrp3/lib/rfnoc/crossbar/chdr_crossbar_nxn.v @@ -0,0 +1,381 @@ +// +// Copyright 2018 Ettus Research, A National Instruments Company +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// +// Module: chdr_crossbar_nxn +// Description:  +//   This module implements a full-bandwidth NxN crossbar with N input and output ports +//   for CHDR traffic. It supports multiple optimization strategies for performance, +//   area and timing tradeoffs. It uses AXI-Stream for all of its links. The crossbar +//   has a dynamic routing table based on a Content Addressable Memory (CAM). The SID +//   is used to determine the destination of a packet and the routing table contains +//   a re-programmable SID to crossbar port mapping. The table is programmed using +//   special route config packets on the data input ports or using an optional +//   management port. +//   The topology, routing algorithms and the router architecture is  +//   described in README.md in this directory.  +// Parameters: +//   - CHDR_W: Width of the AXI-Stream data bus +//   - NPORTS: Number of ports to instantiate +//   - DEFAULT_PORT: The failsafe port to forward a packet to is SID mapping is missing +//   - MTU: log2 of max packet size (in words) +//   - ROUTE_TBL_SIZE: log2 of the number of mappings that the routing table can hold  +//     at any time. Mapping values are maintained in a FIFO fashion. +//   - MUX_ALLOC: Algorithm to allocate the egress MUX +//     * PRIO: Priority based. Lower port numbers have a higher priority +//     * ROUND-ROBIN: Round robin input port allocation +//   - OPTIMIZE: Optimization strategy for performance vs area vs timing tradeoffs +//     * AREA: Attempt to minimize area at the cost of performance (throughput) and/or timing +//     * PERFORMANCE: Attempt to maximize performance at the cost of area and/or timing +//     * TIMING: Attempt to maximize Fmax at the cost of area and/or performance +//   - NPORTS_MGMT: Number of ports with management endpoint. The first NPORTS_MGMT ports will +//     have the management port instantiated +//   - EXT_RTCFG_PORT: Enable a side-channel AXI-Stream management port to configure the +//     routing table +// Signals: +//   - s_axis_*: Slave port for router (flattened) +//   - m_axis_*: Master port for router (flattened) +//   - s_axis_mgmt_*: Management slave port +//   - device_id: The ID of the device that has instantiated this module +// + +module chdr_crossbar_nxn #( +  parameter [15:0] PROTOVER       = {8'd1, 8'd0}, +  parameter        CHDR_W         = 64, +  parameter [7:0]  NPORTS         = 8, +  parameter [7:0]  DEFAULT_PORT   = 0, +  parameter        MTU            = 9, +  parameter        ROUTE_TBL_SIZE = 6, +  parameter        MUX_ALLOC      = "ROUND-ROBIN", +  parameter        OPTIMIZE       = "AREA", +  parameter [7:0]  NPORTS_MGMT    = NPORTS, +  parameter [0:0]  EXT_RTCFG_PORT = 0 +) ( +  input  wire                       clk, +  input  wire                       reset, +  // Device info +  input  wire [15:0]                device_id, +  // Inputs +  input  wire [(CHDR_W*NPORTS)-1:0] s_axis_tdata, +  input  wire [NPORTS-1:0]          s_axis_tlast, +  input  wire [NPORTS-1:0]          s_axis_tvalid, +  output wire [NPORTS-1:0]          s_axis_tready, +  // Output +  output wire [(CHDR_W*NPORTS)-1:0] m_axis_tdata, +  output wire [NPORTS-1:0]          m_axis_tlast, +  output wire [NPORTS-1:0]          m_axis_tvalid, +  input  wire [NPORTS-1:0]          m_axis_tready, +  // Router config management port +  input  wire                       ext_rtcfg_stb, +  input  wire [15:0]                ext_rtcfg_addr, +  input  wire [31:0]                ext_rtcfg_data, +  output wire                       ext_rtcfg_ack +); +  // --------------------------------------------------- +  //  RFNoC Includes +  // --------------------------------------------------- +  `include "../core/rfnoc_chdr_utils.vh" +  `include "../core/rfnoc_chdr_internal_utils.vh" + +  localparam        NPORTS_W = $clog2(NPORTS); +  localparam        EPID_W   = 16; +  localparam [17:0] EXT_INFO = {1'b0, EXT_RTCFG_PORT, NPORTS_MGMT, NPORTS}; + +  localparam [0:0] PKT_ST_HEAD = 1'b0; +  localparam [0:0] PKT_ST_BODY = 1'b1; + +  // The compute_mux_alloc function is the switch allocation function for the MUX  +  // i.e. it chooses which input port reserves the output MUX for packet transfer. +  function [NPORTS_W-1:0] compute_mux_alloc; +    input [NPORTS-1:0] pkt_waiting; +    input [NPORTS_W-1:0] last_alloc; +    reg signed [NPORTS_W:0] i; +  begin +    compute_mux_alloc = last_alloc; +    for (i = NPORTS-1; i >= 0; i=i-1) begin +      if (MUX_ALLOC == "PRIO") begin +        // Priority. Lower port index gets a higher priority. +        if (pkt_waiting[i]) +          compute_mux_alloc = i; +      end else begin +        // Round-robin +        if (pkt_waiting[(last_alloc + i + 1) % NPORTS]) +          compute_mux_alloc = (last_alloc + i + 1) % NPORTS; +      end +    end +  end +  endfunction + +  wire [NPORTS-1:0]            rtcfg_req_wr; +  wire [(16*NPORTS)-1:0]       rtcfg_req_addr; +  wire [(32*NPORTS)-1:0]       rtcfg_req_data; +  wire [NPORTS-1:0]            rtcfg_resp_ack; +  wire [(EPID_W*NPORTS)-1:0]   find_tdata; +  wire [NPORTS-1:0]            find_tvalid; +  wire [NPORTS-1:0]            find_tready; +  wire [(NPORTS_W*NPORTS)-1:0] result_tdata; +  wire [NPORTS-1:0]            result_tkeep; +  wire [NPORTS-1:0]            result_tvalid; +  wire [NPORTS-1:0]            result_tready; + +  // Instantiate a single CAM-based routing table that will be shared between all +  // input ports. Configuration and lookup is performed using an AXI-Stream iface. +  // If multiple packets arrive simultaneously, only the headers of those packets will +  // be serialized in order to arbitrate this map. Selection is done round-robin. +  chdr_xb_routing_table #( +    .SIZE(ROUTE_TBL_SIZE), .NPORTS(NPORTS), +    .EXT_INS_PORT_EN(EXT_RTCFG_PORT) +  ) routing_tbl_i ( +    .clk               (clk           ), +    .reset             (reset         ), +    .port_req_wr       (rtcfg_req_wr  ), +    .port_req_addr     (rtcfg_req_addr), +    .port_req_data     (rtcfg_req_data), +    .port_resp_ack     (rtcfg_resp_ack), +    .ext_req_wr        (ext_rtcfg_stb ), +    .ext_req_addr      (ext_rtcfg_addr), +    .ext_req_data      (ext_rtcfg_data), +    .ext_resp_ack      (ext_rtcfg_ack ), +    .axis_find_tdata   (find_tdata    ), +    .axis_find_tvalid  (find_tvalid   ), +    .axis_find_tready  (find_tready   ), +    .axis_result_tdata (result_tdata  ), +    .axis_result_tkeep (result_tkeep  ), +    .axis_result_tvalid(result_tvalid ), +    .axis_result_tready(result_tready ) +  ); + +  wire [CHDR_W-1:0]          i_tdata   [0:NPORTS-1]; +  wire [9:0]                 i_tdest   [0:NPORTS-1]; +  wire [1:0]                 i_tid     [0:NPORTS-1]; +  wire                       i_tlast   [0:NPORTS-1]; +  wire                       i_tvalid  [0:NPORTS-1]; +  wire                       i_tready  [0:NPORTS-1]; +  wire [CHDR_W-1:0]          buf_tdata [0:NPORTS-1]; +  wire [NPORTS_W-1:0]        buf_tdest [0:NPORTS-1], buf_tdest_tmp[0:NPORTS-1]; +  wire                       buf_tkeep [0:NPORTS-1]; +  wire                       buf_tlast [0:NPORTS-1]; +  wire                       buf_tvalid[0:NPORTS-1]; +  wire                       buf_tready[0:NPORTS-1]; +  wire [CHDR_W-1:0]          swi_tdata [0:NPORTS-1]; +  wire [NPORTS_W-1:0]        swi_tdest [0:NPORTS-1]; +  wire                       swi_tlast [0:NPORTS-1]; +  wire                       swi_tvalid[0:NPORTS-1]; +  wire                       swi_tready[0:NPORTS-1]; +  wire [(CHDR_W*NPORTS)-1:0] swo_tdata [0:NPORTS-1], muxi_tdata [0:NPORTS-1]; +  wire [NPORTS-1:0]          swo_tlast [0:NPORTS-1], muxi_tlast [0:NPORTS-1]; +  wire [NPORTS-1:0]          swo_tvalid[0:NPORTS-1], muxi_tvalid[0:NPORTS-1]; +  wire [NPORTS-1:0]          swo_tready[0:NPORTS-1], muxi_tready[0:NPORTS-1]; + +  genvar n, i, j; +  generate +    for (n = 0; n < NPORTS; n = n + 1) begin: i_ports +      // For each input port, first check if we have a management packet +      // arriving. If it arrives, the top config commands are extrated, sent to the +      // routing table for configuration, and the rest of the packet is forwarded +      // down to the router. +      // the router. +      if (n < NPORTS_MGMT) begin +        chdr_mgmt_pkt_handler #( +          .PROTOVER(PROTOVER), .CHDR_W(CHDR_W), .MGMT_ONLY(0) +        ) mgmt_ep_i ( +          .clk                (clk                                                              ), +          .rst                (reset                                                            ), +          .node_info          (chdr_mgmt_build_node_info(EXT_INFO, n, NODE_TYPE_XBAR, device_id)), +          .s_axis_chdr_tdata  (s_axis_tdata [(n*CHDR_W)+:CHDR_W]                                ), +          .s_axis_chdr_tlast  (s_axis_tlast [n]                                                 ), +          .s_axis_chdr_tvalid (s_axis_tvalid[n]                                                 ), +          .s_axis_chdr_tready (s_axis_tready[n]                                                 ), +          .s_axis_chdr_tuser  ('d0                                                              ), +          .m_axis_chdr_tdata  (i_tdata      [n]                                                 ), +          .m_axis_chdr_tdest  (i_tdest      [n]                                                 ),  +          .m_axis_chdr_tid    (i_tid        [n]                                                 ), +          .m_axis_chdr_tlast  (i_tlast      [n]                                                 ), +          .m_axis_chdr_tvalid (i_tvalid     [n]                                                 ), +          .m_axis_chdr_tready (i_tready     [n]                                                 ), +          .ctrlport_req_wr    (rtcfg_req_wr [n]                                                 ), +          .ctrlport_req_rd    (/* unused */                                                     ), +          .ctrlport_req_addr  (rtcfg_req_addr[(n*16)+:16]                                       ), +          .ctrlport_req_data  (rtcfg_req_data[(n*32)+:32]                                       ), +          .ctrlport_resp_ack  (rtcfg_resp_ack[n]                                                ), +          .ctrlport_resp_data (32'h0 /* unused */                                               ), +          .op_stb             (/* unused */                                                     ), +          .op_dst_epid        (/* unused */                                                     ), +          .op_src_epid        (/* unused */                                                     ), +          .op_data            (/* unused */                                                     ) +        ); +      end else begin +        assign i_tdata      [n] = s_axis_tdata [(n*CHDR_W)+:CHDR_W]; +        assign i_tid        [n] = CHDR_MGMT_ROUTE_EPID; +        assign i_tdest      [n] = 10'd0;  // Unused +        assign i_tlast      [n] = s_axis_tlast [n]; +        assign i_tvalid     [n] = s_axis_tvalid[n]; +        assign s_axis_tready[n] = i_tready     [n]; + +        assign rtcfg_req_wr  [n]          =  1'b0; +        assign rtcfg_req_addr[(n*16)+:16] = 16'h0; +        assign rtcfg_req_data[(n*32)+:32] = 32'h0; +      end + +      // Ingress buffer module that does the following: +      // - Stores and gates an incoming packet +      // - Looks up destination in routing table and attaches a tdest for the packet +      chdr_xb_ingress_buff #( +        .WIDTH(CHDR_W), .MTU(MTU), .DEST_W(NPORTS_W), .NODE_ID(n) +      ) buf_i ( +        .clk                 (clk                                  ), +        .reset               (reset                                ), +        .s_axis_chdr_tdata   (i_tdata      [n]                     ), +        .s_axis_chdr_tdest   (i_tdest      [n][NPORTS_W-1:0]       ), +        .s_axis_chdr_tid     (i_tid        [n]                     ), +        .s_axis_chdr_tlast   (i_tlast      [n]                     ), +        .s_axis_chdr_tvalid  (i_tvalid     [n]                     ), +        .s_axis_chdr_tready  (i_tready     [n]                     ), +        .m_axis_chdr_tdata   (buf_tdata    [n]                     ), +        .m_axis_chdr_tdest   (buf_tdest_tmp[n]                     ), +        .m_axis_chdr_tkeep   (buf_tkeep    [n]                     ), +        .m_axis_chdr_tlast   (buf_tlast    [n]                     ), +        .m_axis_chdr_tvalid  (buf_tvalid   [n]                     ), +        .m_axis_chdr_tready  (buf_tready   [n]                     ), +        .m_axis_find_tdata   (find_tdata   [(n*EPID_W)+:EPID_W]    ), +        .m_axis_find_tvalid  (find_tvalid  [n]                     ), +        .m_axis_find_tready  (find_tready  [n]                     ), +        .s_axis_result_tdata (result_tdata [(n*NPORTS_W)+:NPORTS_W]), +        .s_axis_result_tkeep (result_tkeep [n]                     ), +        .s_axis_result_tvalid(result_tvalid[n]                     ), +        .s_axis_result_tready(result_tready[n]                     ) +      ); +      assign buf_tdest[n] = buf_tkeep[n] ? buf_tdest_tmp[n] : DEFAULT_PORT[NPORTS_W-1:0]; + +      // Pipeline state +      axi_fifo #( +        .WIDTH(CHDR_W+1+NPORTS_W), .SIZE(1) +      ) pipe_i ( +        .clk      (clk                                       ),  +        .reset    (reset                                     ),  +        .clear    (1'b0                                      ), +        .i_tdata  ({buf_tlast[n], buf_tdest[n], buf_tdata[n]}), +        .i_tvalid (buf_tvalid[n]                             ), +        .i_tready (buf_tready[n]                             ), +        .o_tdata  ({swi_tlast[n], swi_tdest[n], swi_tdata[n]}), +        .o_tvalid (swi_tvalid[n]                             ), +        .o_tready (swi_tready[n]                             ), +        .space    (/* Unused */                              ), +        .occupied (/* Unused */                              ) +      ); + +      // Ingress demux. Use the tdest field to determine packet destination +      axis_switch #( +        .DATA_W(CHDR_W), .DEST_W(1), .IN_PORTS(1), .OUT_PORTS(NPORTS), .PIPELINE(1) +      ) demux_i ( +        .clk           (clk                  ), +        .reset         (reset                ), +        .s_axis_tdata  (swi_tdata [n]        ), +        .s_axis_tdest  ({1'b0, swi_tdest [n]}),  +        .s_axis_tlast  (swi_tlast [n]        ), +        .s_axis_tvalid (swi_tvalid[n]        ), +        .s_axis_tready (swi_tready[n]        ), +        .s_axis_alloc  (1'b0                 ), +        .m_axis_tdata  (swo_tdata [n]        ), +        .m_axis_tdest  (/* Unused */         ), +        .m_axis_tlast  (swo_tlast [n]        ), +        .m_axis_tvalid (swo_tvalid[n]        ), +        .m_axis_tready (swo_tready[n]        ) +      ); +    end + +    for (i = 0; i < NPORTS; i = i + 1) begin +      for (j = 0; j < NPORTS; j = j + 1) begin +        assign muxi_tdata [i][j*CHDR_W+:CHDR_W] = swo_tdata  [j][i*CHDR_W+:CHDR_W]; +        assign muxi_tlast [i][j]                = swo_tlast  [j][i]; +        assign muxi_tvalid[i][j]                = swo_tvalid [j][i]; +        assign swo_tready [i][j]                = muxi_tready[j][i]; +      end +    end + +    for (n = 0; n < NPORTS; n = n + 1) begin: o_ports +      if (OPTIMIZE == "PERFORMANCE") begin +        // Use the axis_switch module when optimizing for performance +        // This logic has some extra levels of logic to ensure +        // that the switch allocation happens in 0 clock cycles which +        // means that Fmax for this implementation will be lower. + +        wire mux_ready = |muxi_tready[n];   // Max 1 bit should be high +        wire mux_valid = |muxi_tvalid[n]; +        wire mux_last  = |(muxi_tvalid[n] & muxi_tlast[n]); +   +        // Track the input packet state +        reg [0:0] pkt_state = PKT_ST_HEAD; +        always @(posedge clk) begin +          if (reset) begin +            pkt_state <= PKT_ST_HEAD; +          end else if (mux_valid & mux_ready) begin +            pkt_state <= mux_last ? PKT_ST_HEAD : PKT_ST_BODY; +          end +        end +   +        // The switch requires the allocation to stay valid until the +        // end of the packet. We also might need to keep the previous +        // packet's allocation to compute the current one +        reg  [NPORTS_W-1:0] prev_sw_alloc = {NPORTS_W{1'b0}}; +        reg  [NPORTS_W-1:0] pkt_sw_alloc  = {NPORTS_W{1'b0}}; +        wire [NPORTS_W-1:0] muxi_sw_alloc = (mux_valid && pkt_state == PKT_ST_HEAD) ?  +          compute_mux_alloc(muxi_tvalid[n], prev_sw_alloc) : pkt_sw_alloc; +   +        always @(posedge clk) begin +          if (reset) begin +            prev_sw_alloc <= {NPORTS_W{1'b0}}; +            pkt_sw_alloc <= {NPORTS_W{1'b0}}; +          end else if (mux_valid & mux_ready) begin +            if (pkt_state == PKT_ST_HEAD) +              pkt_sw_alloc <= muxi_sw_alloc; +            if (mux_last) +              prev_sw_alloc <= muxi_sw_alloc; +          end +        end +   +        axis_switch #( +          .DATA_W(CHDR_W), .DEST_W(1), .IN_PORTS(NPORTS), .OUT_PORTS(1), +          .PIPELINE(0) +        ) mux_i ( +          .clk           (clk                              ), +          .reset         (reset                            ), +          .s_axis_tdata  (muxi_tdata [n]                   ), +          .s_axis_tdest  ({NPORTS{1'b0}} /* Unused */      ), +          .s_axis_tlast  (muxi_tlast [n]                   ), +          .s_axis_tvalid (muxi_tvalid[n]                   ), +          .s_axis_tready (muxi_tready[n]                   ), +          .s_axis_alloc  (muxi_sw_alloc                    ), +          .m_axis_tdata  (m_axis_tdata [(n*CHDR_W)+:CHDR_W]), +          .m_axis_tdest  (/* Unused */                     ), +          .m_axis_tlast  (m_axis_tlast [n]                 ), +          .m_axis_tvalid (m_axis_tvalid[n]                 ), +          .m_axis_tready (m_axis_tready[n]                 ) +        ); +      end else begin +        // axi_mux has an additional bubble cycle but the logic +        // to allocate an input port has fewer levels and takes +        // up fewer resources. +        axi_mux #( +          .PRIO(MUX_ALLOC == "PRIO"), .WIDTH(CHDR_W), .SIZE(NPORTS), +          .PRE_FIFO_SIZE(OPTIMIZE == "TIMING" ? 1 : 0), .POST_FIFO_SIZE(1) +        ) mux_i ( +          .clk      (clk                              ), +          .reset    (reset                            ), +          .clear    (1'b0                             ), +          .i_tdata  (muxi_tdata   [n]                 ), +          .i_tlast  (muxi_tlast   [n]                 ), +          .i_tvalid (muxi_tvalid  [n]                 ), +          .i_tready (muxi_tready  [n]                 ), +          .o_tdata  (m_axis_tdata [(n*CHDR_W)+:CHDR_W]), +          .o_tlast  (m_axis_tlast [n]                 ), +          .o_tvalid (m_axis_tvalid[n]                 ), +          .o_tready (m_axis_tready[n]                 ) +        ); +      end +    end +  endgenerate + + +endmodule diff --git a/fpga/usrp3/lib/rfnoc/crossbar/chdr_xb_ingress_buff.v b/fpga/usrp3/lib/rfnoc/crossbar/chdr_xb_ingress_buff.v new file mode 100644 index 000000000..dcb11da8e --- /dev/null +++ b/fpga/usrp3/lib/rfnoc/crossbar/chdr_xb_ingress_buff.v @@ -0,0 +1,259 @@ +// +// Copyright 2018 Ettus Research, A National Instruments Company +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// +// Module: chdr_ingress_buff +// +// Description: +// +//   Ingress buffer module for the CHDR crossbar. This module stores and gates +//   the incoming packet and simultaneously determines the destination (TDEST) +//   by inspecting the incoming TID. If the TID is CHDR_MGMT_ROUTE_EPID then we +//   perform a lookup on the TID to determine the correct output for TDEST. +// +// Parameters: +// +//   WIDTH   : Data width of the CHDR interfaces (TDATA) +//   MTU     : Maximum transmission unit, in WIDTH-sized words, is 2**MTU +//   DEST_W  : Width of the destination routing information (TDEST) +//   NODE_ID : Numeric identifier for this port +// + +module chdr_xb_ingress_buff #( +  parameter       WIDTH   = 64, +  parameter       MTU     = 5, +  parameter       DEST_W  = 4, +  parameter [9:0] NODE_ID = 0 +) ( +  input  wire               clk, +  input  wire               reset, +  // CHDR input port +  input  wire [WIDTH-1:0]   s_axis_chdr_tdata, +  input  wire [DEST_W-1:0]  s_axis_chdr_tdest, +  input  wire [1:0]         s_axis_chdr_tid, +  input  wire               s_axis_chdr_tlast, +  input  wire               s_axis_chdr_tvalid, +  output wire               s_axis_chdr_tready, +  // CHDR output port (with a tdest and tkeep) +  output wire [WIDTH-1:0]   m_axis_chdr_tdata, +  output wire [DEST_W-1:0]  m_axis_chdr_tdest, +  output wire               m_axis_chdr_tkeep, +  output wire               m_axis_chdr_tlast, +  output wire               m_axis_chdr_tvalid, +  input  wire               m_axis_chdr_tready, +  // Find port going to routing table +  output wire [15:0]        m_axis_find_tdata, +  output wire               m_axis_find_tvalid, +  input  wire               m_axis_find_tready, +  // Result port from routing table +  input  wire [DEST_W-1:0]  s_axis_result_tdata, +  input  wire               s_axis_result_tkeep, +  input  wire               s_axis_result_tvalid, +  output wire               s_axis_result_tready +); + +  // RFNoC Includes +  `include "../core/rfnoc_chdr_utils.vh" +  `include "../core/rfnoc_chdr_internal_utils.vh" + + +  //--------------------------------------------------------------------------- +  // Packet Buffer +  //--------------------------------------------------------------------------- + +  wire [WIDTH-1:0]  gate_i_tdata , gate_o_tdata ; +  wire              gate_i_tlast , gate_o_tlast ; +  wire              gate_i_tvalid, gate_o_tvalid; +  wire              gate_i_tready, gate_o_tready; + +  // The axi_packet_gate queues up an entire packet before letting it go out. +  // This reduces congestion in the crossbar for slowly-built packets. +  axi_packet_gate #( +    .WIDTH (WIDTH), +    .SIZE  (MTU) +  ) axi_packet_gate_i ( +    .clk      (clk), +    .reset    (reset), +    .clear    (1'b0), +    .i_tdata  (gate_i_tdata), +    .i_tlast  (gate_i_tlast), +    .i_terror (1'b0), +    .i_tvalid (gate_i_tvalid), +    .i_tready (gate_i_tready), +    .o_tdata  (gate_o_tdata), +    .o_tlast  (gate_o_tlast), +    .o_tvalid (gate_o_tvalid), +    .o_tready (gate_o_tready) +  ); + + +  //--------------------------------------------------------------------------- +  // Destination (TDEST) Muxing +  //--------------------------------------------------------------------------- + +  wire [15:0]       find_tdata; +  wire              find_tvalid, find_tready; + +  wire [DEST_W-1:0] dest_i_tdata; +  wire              dest_i_tkeep, dest_i_tvalid, dest_i_tready; +  wire [DEST_W-1:0] dest_o_tdata; +  wire              dest_o_tkeep, dest_o_tvalid, dest_o_tready; + +  // The find_fifo holds the lookup requests from the find_* AXI stream and +  // sends them on to the m_axis_find_* stream port. It is required because the +  // input logic (see below) doesn't obey the AXI handshake protocol but this +  // FIFO can tolerate it. +  axi_fifo #( +    .WIDTH (16), +    .SIZE  (1) +  ) find_fifo_i ( +    .clk      (clk), +    .reset    (reset), +    .clear    (1'b0), +    .i_tdata  (find_tdata), +    .i_tvalid (find_tvalid), +    .i_tready (find_tready), +    .o_tdata  (m_axis_find_tdata), +    .o_tvalid (m_axis_find_tvalid), +    .o_tready (m_axis_find_tready), +    .space    (), +    .occupied () +  ); + +  // The destination (TDEST) can come from two sources: Directly from the +  // packet info (in which case TDEST was immediately determined and comes in +  // on dest_* AXI stream) or via a lookup (in which case the result comes in +  // on s_axis_result_*). Only one of these data paths is used at a time, so we +  // mux them together here create a single stream (dest_o_*) that contains the +  // destination for the next packet. +  axi_mux #( +    .WIDTH          (DEST_W+1), +    .SIZE           (2), +    .PRIO           (1), +    .PRE_FIFO_SIZE  (1), +    .POST_FIFO_SIZE (1) +  ) dest_mux_i ( +    .clk      (clk), +    .reset    (reset), +    .clear    (1'b0), +    .i_tdata  ({dest_i_tkeep, dest_i_tdata,  +                s_axis_result_tkeep, s_axis_result_tdata}), +    .i_tlast  (2'b11), +    .i_tvalid ({dest_i_tvalid, s_axis_result_tvalid}), +    .i_tready ({dest_i_tready, s_axis_result_tready}), +    .o_tdata  ({dest_o_tkeep, dest_o_tdata}), +    .o_tlast  (), +    .o_tvalid (dest_o_tvalid), +    .o_tready (dest_o_tready) +  ); + + +  //--------------------------------------------------------------------------- +  // Input Logic +  //--------------------------------------------------------------------------- +  // +  // When a packet comes in, we may have to do one of the following: +  //   1) Lookup the TDEST using the EPID +  //   2) Use the specified input TDEST +  //   3) Use the NODE_ID as the TDEST (to return the packet) +  // +  //--------------------------------------------------------------------------- + +  // The s_axis_chdr_hdr_valid signal indicates when TDATA and TID contain the +  // header information for the current packet. +  reg s_axis_chdr_hdr_valid = 1'b1; + +  always @(posedge clk) begin +    if (reset) begin +      s_axis_chdr_hdr_valid <= 1'b1; +    end else if (s_axis_chdr_tvalid & s_axis_chdr_tready) begin +      s_axis_chdr_hdr_valid <= s_axis_chdr_tlast; +    end +  end + +  // The dest_find_tready signal indicates if the find_fifo is ready or if the +  // dest port of the dest_muax is ready, depending on which path will be used. +  reg dest_find_tready; + +  always @(*) begin +    if (s_axis_chdr_hdr_valid) begin +      case (s_axis_chdr_tid) +        CHDR_MGMT_ROUTE_EPID: +          dest_find_tready = find_tready; +        CHDR_MGMT_ROUTE_TDEST: +          dest_find_tready = dest_i_tready; +        CHDR_MGMT_RETURN_TO_SRC: +          dest_find_tready = dest_i_tready; +        default: +          dest_find_tready = dest_i_tready; // We should never get here +      endcase +    end else begin +      dest_find_tready = 1'b1; +    end +  end + +  // We can accept a transfer from the input CHDR stream only if the the packet +  // gate and dest/find datapaths are ready. +  assign s_axis_chdr_tready = s_axis_chdr_tvalid && +                              gate_i_tready      && +                              dest_find_tready; + +  // The chdr_header_stb signal indicates when we write data into the dest/find +  // data path. This happens when we're accepting the header word of the packet +  // into the packet gate. +  wire chdr_header_stb = s_axis_chdr_tvalid && +                         s_axis_chdr_tready && +                         s_axis_chdr_hdr_valid; + +  // ************************************************************************** +  // WARNING: The logic below violates AXI-Stream by having a tready -> tvalid +  //          dependency To ensure no deadlocks, we must place FIFOs downstream +  //          of gate_i_*, find_* and dest_i_* + +  // Here we decide if we need to do a lookup using the find_* path or if the +  // destination is known and can be put directly on the dest_* path. +  // +  // Start a lookup request if the TID is CHDR_MGMT_ROUTE_EPID. +  assign find_tdata  = chdr_get_dst_epid(s_axis_chdr_tdata[63:0]); +  assign find_tvalid = chdr_header_stb &&  +                       (s_axis_chdr_tid == CHDR_MGMT_ROUTE_EPID); +  // Set TDEST directly if TID is CHDR_MGMT_ROUTE_TDEST or +  // CHDR_MGMT_RETURN_TO_SRC. +  assign dest_i_tdata  = (s_axis_chdr_tid == CHDR_MGMT_ROUTE_TDEST) ?  +                         s_axis_chdr_tdest : NODE_ID[DEST_W-1:0]; +  assign dest_i_tkeep  = 1'b1; +  assign dest_i_tvalid = chdr_header_stb &&  +                         (s_axis_chdr_tid != CHDR_MGMT_ROUTE_EPID); + +  // Input logic for axi_packet_gate +  assign gate_i_tdata  = s_axis_chdr_tdata; +  assign gate_i_tlast  = s_axis_chdr_tlast; +  assign gate_i_tvalid = s_axis_chdr_tready && s_axis_chdr_tvalid; + +  // +  // ************************************************************************** + + +  //--------------------------------------------------------------------------- +  // Output Logic +  //--------------------------------------------------------------------------- +  // +  // The destination for the packet (TDEST) must be valid before we allow the +  // header of the packet to pass through. So the packet must be blocked until +  // the output of the dest_o_* is valid. TDEST and TKEEP must remain valid +  // until the end of the packet. +  // +  //--------------------------------------------------------------------------- + +  assign m_axis_chdr_tdata  = gate_o_tdata; +  assign m_axis_chdr_tlast  = gate_o_tlast; +  assign m_axis_chdr_tdest  = dest_o_tdata; +  assign m_axis_chdr_tkeep  = dest_o_tkeep; +  assign m_axis_chdr_tvalid = gate_o_tvalid && dest_o_tvalid; + +  assign gate_o_tready = m_axis_chdr_tvalid && m_axis_chdr_tready; +  assign dest_o_tready = m_axis_chdr_tvalid && m_axis_chdr_tready && m_axis_chdr_tlast; + +endmodule + diff --git a/fpga/usrp3/lib/rfnoc/crossbar/chdr_xb_routing_table.v b/fpga/usrp3/lib/rfnoc/crossbar/chdr_xb_routing_table.v new file mode 100644 index 000000000..f445efc68 --- /dev/null +++ b/fpga/usrp3/lib/rfnoc/crossbar/chdr_xb_routing_table.v @@ -0,0 +1,122 @@ +// +// Copyright 2018 Ettus Research, A National Instruments Company +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// +// Module: chdr_xb_routing_table +// Description: +//   A routing table for the CHDR crossbar. This table is designed +//   to be shared between all ports. It has an AXI-Stream lookup +//   interface and a ctrlport (reduced) configuration interface. + +module chdr_xb_routing_table #( +  parameter SIZE            = 6, +  parameter NPORTS          = 4, +  parameter EXT_INS_PORT_EN = 1 +) ( +  // Clocks and resets +  input  wire                               clk, +  input  wire                               reset, +  // Insertion Interface (for XB ports) +  input  wire [NPORTS-1:0]                  port_req_wr, +  input  wire [(16*NPORTS)-1:0]             port_req_addr, +  input  wire [(32*NPORTS)-1:0]             port_req_data, +  output wire [NPORTS-1:0]                  port_resp_ack, +  // Insertion Interface (External)          +  input  wire                               ext_req_wr, +  input  wire [15:0]                        ext_req_addr, +  input  wire [31:0]                        ext_req_data, +  output wire                               ext_resp_ack, +  // Find Interface                          +  input  wire [(16*NPORTS)-1:0]             axis_find_tdata, +  input  wire [NPORTS-1:0]                  axis_find_tvalid, +  output wire [NPORTS-1:0]                  axis_find_tready, +  // Result Interface (for Find) +  output wire [($clog2(NPORTS)*NPORTS)-1:0] axis_result_tdata, +  output wire [NPORTS-1:0]                  axis_result_tkeep, +  output wire [NPORTS-1:0]                  axis_result_tvalid, +  input  wire [NPORTS-1:0]                  axis_result_tready +); +  localparam NPORTS_W   = $clog2(NPORTS); +  localparam CFG_W      = NPORTS_W + 16; +  localparam CFG_PORTS  = NPORTS + EXT_INS_PORT_EN; + +  // CAM-based lookup table + +  wire [15:0]         insert_tdest; +  wire [NPORTS_W-1:0] insert_tdata; +  wire                insert_tvalid; +  wire                insert_tready; + +  axis_muxed_kv_map #( +    .KEY_WIDTH(16), .VAL_WIDTH(NPORTS_W), +    .SIZE(SIZE), .NUM_PORTS(NPORTS) +  ) kv_map_i ( +    .clk               (clk               ), +    .reset             (reset               ), +    .axis_insert_tdata (insert_tdata      ), +    .axis_insert_tdest (insert_tdest      ), +    .axis_insert_tvalid(insert_tvalid     ), +    .axis_insert_tready(insert_tready     ), +    .axis_find_tdata   (axis_find_tdata   ), +    .axis_find_tvalid  (axis_find_tvalid  ), +    .axis_find_tready  (axis_find_tready  ), +    .axis_result_tdata (axis_result_tdata ), +    .axis_result_tkeep (axis_result_tkeep ), +    .axis_result_tvalid(axis_result_tvalid), +    .axis_result_tready(axis_result_tready) +  ); + +  // Logic to convert from ctrlport to AXI-Stream + +  wire                ins_req_wr  [0:CFG_PORTS-1]; +  wire [15:0]         ins_req_addr[0:CFG_PORTS-1]; +  wire [NPORTS_W-1:0] ins_req_data[0:CFG_PORTS-1]; +  wire                ins_resp_ack[0:CFG_PORTS-1]; + +  reg  [(CFG_PORTS*CFG_W)-1:0] cfg_tdata; +  reg  [CFG_PORTS-1:0] cfg_tvalid = {CFG_PORTS{1'b0}}; +  wire [CFG_PORTS-1:0] cfg_tready; + +  genvar i; +  generate for (i = 0; i < CFG_PORTS; i=i+1) begin +    assign ins_req_wr  [i] = (i < NPORTS) ? port_req_wr[i]                  : ext_req_wr; +    assign ins_req_addr[i] = (i < NPORTS) ? port_req_addr[i*16 +: 16]       : ext_req_addr; +    assign ins_req_data[i] = (i < NPORTS) ? port_req_data[i*32 +: NPORTS_W] : ext_req_data[NPORTS_W-1:0]; +    if (i < NPORTS) +      assign port_resp_ack[i] = ins_resp_ack[i]; +    else +      assign ext_resp_ack     = ins_resp_ack[i]; + +    always @(posedge clk) begin +      if (reset) begin +        cfg_tvalid[i] <= 1'b0; +      end else begin +        if (~cfg_tvalid[i]) begin +          if (ins_req_wr[i]) begin +            cfg_tvalid[i] <= 1'b1; +            cfg_tdata[(CFG_W*i) +: CFG_W] <= {ins_req_data[i], ins_req_addr[i]}; +          end +        end else begin +          cfg_tvalid[i] <= ~cfg_tready[i]; +        end +      end +    end +    assign ins_resp_ack[i] = cfg_tvalid[i] & cfg_tready[i]; +  end endgenerate + +  // Multiplexer between XB ports and external cfg + +  axi_mux #( +    .WIDTH(CFG_W), .SIZE(CFG_PORTS), +    .PRE_FIFO_SIZE(0), .POST_FIFO_SIZE(1) +  ) rtcfg_mux_i ( +    .clk(clk), .reset(reset), .clear(1'b0), +    .i_tdata(cfg_tdata), .i_tlast({(NPORTS_W + 16){1'b1}}), +    .i_tvalid(cfg_tvalid), .i_tready(cfg_tready), +    .o_tdata({insert_tdata, insert_tdest}), .o_tlast(), +    .o_tvalid(insert_tvalid), .o_tready(insert_tready) +  ); + +endmodule + diff --git a/fpga/usrp3/lib/rfnoc/crossbar/crossbar_tb/Makefile b/fpga/usrp3/lib/rfnoc/crossbar/crossbar_tb/Makefile new file mode 100644 index 000000000..7fa7ae03b --- /dev/null +++ b/fpga/usrp3/lib/rfnoc/crossbar/crossbar_tb/Makefile @@ -0,0 +1,52 @@ +# +# Copyright 2015 Ettus Research LLC +# + +#------------------------------------------------- +# Top-of-Makefile +#------------------------------------------------- +# Define BASE_DIR to point to the "top" dir +BASE_DIR = $(abspath ../../../../top) +# Include viv_sim_preamble after defining BASE_DIR +include $(BASE_DIR)/../tools/make/viv_sim_preamble.mak + +#------------------------------------------------- +# Design Specific +#------------------------------------------------- +# Define part using PART_ID (<device>/<package>/<speedgrade>) +ARCH = kintex7 +PART_ID = xc7k410t/ffg900/-2 + +# Include makefiles and sources for the DUT and its dependencies +include $(BASE_DIR)/../lib/control/Makefile.srcs +include $(BASE_DIR)/../lib/fifo/Makefile.srcs +include $(BASE_DIR)/../lib/rfnoc/crossbar/Makefile.srcs +include $(BASE_DIR)/../lib/rfnoc/core/Makefile.srcs + +DESIGN_SRCS = $(abspath \ +$(FIFO_SRCS) \ +$(CONTROL_LIB_SRCS) \ +$(RFNOC_XBAR_SRCS) \ +$(RFNOC_CORE_SRCS) \ +) + +#------------------------------------------------- +# Testbench Specific +#------------------------------------------------- +# Define only one toplevel module +TB_TOP_MODULE ?= crossbar_tb +SIM_TOP = $(TB_TOP_MODULE) + +SIM_SRCS = \ +$(abspath chdr_traffic_source_sim.sv) \ +$(abspath chdr_traffic_sink_sim.sv) \ +$(abspath crossbar_tb.sv) \ +$(abspath $(TB_TOP_MODULE).sv) + +#------------------------------------------------- +# Bottom-of-Makefile +#------------------------------------------------- +# Include all simulator specific makefiles here +# Each should define a unique target to simulate +# e.g. xsim, vsim, etc and a common "clean" target +include $(BASE_DIR)/../tools/make/viv_simulator.mak diff --git a/fpga/usrp3/lib/rfnoc/crossbar/crossbar_tb/axis_ctrl_crossbar_nxn_tb/Makefile b/fpga/usrp3/lib/rfnoc/crossbar/crossbar_tb/axis_ctrl_crossbar_nxn_tb/Makefile new file mode 100644 index 000000000..0f1a10a6e --- /dev/null +++ b/fpga/usrp3/lib/rfnoc/crossbar/crossbar_tb/axis_ctrl_crossbar_nxn_tb/Makefile @@ -0,0 +1,51 @@ +# +# Copyright 2015 Ettus Research LLC +# + +#------------------------------------------------- +# Top-of-Makefile +#------------------------------------------------- +# Define BASE_DIR to point to the "top" dir +BASE_DIR = $(abspath ../../../../../top) +# Include viv_sim_preamble after defining BASE_DIR +include $(BASE_DIR)/../tools/make/viv_sim_preamble.mak + +#------------------------------------------------- +# Design Specific +#------------------------------------------------- +# Define part using PART_ID (<device>/<package>/<speedgrade>) +ARCH = kintex7 +PART_ID = xc7k410t/ffg900/-2 + +# Include makefiles and sources for the DUT and its dependencies +include $(BASE_DIR)/../lib/control/Makefile.srcs +include $(BASE_DIR)/../lib/fifo/Makefile.srcs +include $(BASE_DIR)/../lib/rfnoc/crossbar/Makefile.srcs +include $(BASE_DIR)/../lib/rfnoc/core/Makefile.srcs + +DESIGN_SRCS = $(abspath \ +$(FIFO_SRCS) \ +$(CONTROL_LIB_SRCS) \ +$(RFNOC_XBAR_SRCS) \ +$(RFNOC_CORE_SRCS) \ +) + +#------------------------------------------------- +# Testbench Specific +#------------------------------------------------- +# Define only one toplevel module +SIM_TOP = axis_ctrl_crossbar_nxn_tb + +SIM_SRCS = \ +$(abspath axis_ctrl_crossbar_nxn_tb.sv) \ +$(abspath ../crossbar_tb.sv) \ +$(abspath ../chdr_traffic_source_sim.sv) \ +$(abspath ../chdr_traffic_sink_sim.sv) + +#------------------------------------------------- +# Bottom-of-Makefile +#------------------------------------------------- +# Include all simulator specific makefiles here +# Each should define a unique target to simulate +# e.g. xsim, vsim, etc and a common "clean" target +include $(BASE_DIR)/../tools/make/viv_simulator.mak diff --git a/fpga/usrp3/lib/rfnoc/crossbar/crossbar_tb/axis_ctrl_crossbar_nxn_tb/axis_ctrl_crossbar_nxn_tb.sv b/fpga/usrp3/lib/rfnoc/crossbar/crossbar_tb/axis_ctrl_crossbar_nxn_tb/axis_ctrl_crossbar_nxn_tb.sv new file mode 100644 index 000000000..fa112f5cb --- /dev/null +++ b/fpga/usrp3/lib/rfnoc/crossbar/crossbar_tb/axis_ctrl_crossbar_nxn_tb/axis_ctrl_crossbar_nxn_tb.sv @@ -0,0 +1,26 @@ +// +// Copyright 2018 Ettus Research, A National Instruments Company +// +// SPDX-License-Identifier: LGPL-3.0-or-later + + +`timescale 1ns/1ps + +module axis_ctrl_crossbar_nxn_tb(); +  crossbar_tb #( +    .TEST_NAME          ("axis_ctrl_crossbar_nxn_tb"), +    .ROUTER_IMPL        ("axis_ctrl_2d_torus"       ), // Router implementation +    .ROUTER_PORTS       (20                         ), // Number of ports +    .ROUTER_DWIDTH      (64                         ), // Router datapath width +    .MTU_LOG2           (5                          ), // log2 of max packet size for router +    .NUM_MASTERS        (4                          ), // Number of data generators in test +    .TEST_MAX_PACKETS   (100                        ), // How many packets to stream per test case? +    .TEST_LPP           (20                         ), // Lines per packet +    .TEST_MIN_INJ_RATE  (10                         ), // Minimum injection rate to test +    .TEST_MAX_INJ_RATE  (40                         ), // Maximum injection rate to test +    .TEST_INJ_RATE_INCR (10                         ), // Injection rate increment +    .TEST_GEN_LL_FILES  (0                          )  // Generate files to produce load-latency graphs? +  ) impl ( +    /* no IO */ +  ); +endmodule diff --git a/fpga/usrp3/lib/rfnoc/crossbar/crossbar_tb/chdr_crossbar_nxn_tb/Makefile b/fpga/usrp3/lib/rfnoc/crossbar/crossbar_tb/chdr_crossbar_nxn_tb/Makefile new file mode 100644 index 000000000..399515640 --- /dev/null +++ b/fpga/usrp3/lib/rfnoc/crossbar/crossbar_tb/chdr_crossbar_nxn_tb/Makefile @@ -0,0 +1,51 @@ +# +# Copyright 2015 Ettus Research LLC +# + +#------------------------------------------------- +# Top-of-Makefile +#------------------------------------------------- +# Define BASE_DIR to point to the "top" dir +BASE_DIR = $(abspath ../../../../../top) +# Include viv_sim_preamble after defining BASE_DIR +include $(BASE_DIR)/../tools/make/viv_sim_preamble.mak + +#------------------------------------------------- +# Design Specific +#------------------------------------------------- +# Define part using PART_ID (<device>/<package>/<speedgrade>) +ARCH = kintex7 +PART_ID = xc7k410t/ffg900/-2 + +# Include makefiles and sources for the DUT and its dependencies +include $(BASE_DIR)/../lib/control/Makefile.srcs +include $(BASE_DIR)/../lib/fifo/Makefile.srcs +include $(BASE_DIR)/../lib/rfnoc/crossbar/Makefile.srcs +include $(BASE_DIR)/../lib/rfnoc/core/Makefile.srcs + +DESIGN_SRCS = $(abspath \ +$(FIFO_SRCS) \ +$(CONTROL_LIB_SRCS) \ +$(RFNOC_XBAR_SRCS) \ +$(RFNOC_CORE_SRCS) \ +) + +#------------------------------------------------- +# Testbench Specific +#------------------------------------------------- +# Define only one toplevel module +SIM_TOP = chdr_crossbar_nxn_tb + +SIM_SRCS = \ +$(abspath chdr_crossbar_nxn_tb.sv) \ +$(abspath ../crossbar_tb.sv) \ +$(abspath ../chdr_traffic_source_sim.sv) \ +$(abspath ../chdr_traffic_sink_sim.sv) + +#------------------------------------------------- +# Bottom-of-Makefile +#------------------------------------------------- +# Include all simulator specific makefiles here +# Each should define a unique target to simulate +# e.g. xsim, vsim, etc and a common "clean" target +include $(BASE_DIR)/../tools/make/viv_simulator.mak diff --git a/fpga/usrp3/lib/rfnoc/crossbar/crossbar_tb/chdr_crossbar_nxn_tb/chdr_crossbar_nxn_tb.sv b/fpga/usrp3/lib/rfnoc/crossbar/crossbar_tb/chdr_crossbar_nxn_tb/chdr_crossbar_nxn_tb.sv new file mode 100644 index 000000000..1c5cace63 --- /dev/null +++ b/fpga/usrp3/lib/rfnoc/crossbar/crossbar_tb/chdr_crossbar_nxn_tb/chdr_crossbar_nxn_tb.sv @@ -0,0 +1,26 @@ +// +// Copyright 2018 Ettus Research, A National Instruments Company +// +// SPDX-License-Identifier: LGPL-3.0-or-later + + +`timescale 1ns/1ps + +module chdr_crossbar_nxn_tb(); +  crossbar_tb #( +    .TEST_NAME          ("chdr_crossbar_nxn_tb"), +    .ROUTER_IMPL        ("chdr_crossbar_nxn"   ), // Router implementation +    .ROUTER_PORTS       (10                    ), // Number of ports +    .ROUTER_DWIDTH      (64                    ), // Router datapath width +    .MTU_LOG2           (7                     ), // log2 of max packet size for router +    .NUM_MASTERS        (10                    ), // Number of data generators in test +    .TEST_MAX_PACKETS   (100                   ), // How many packets to stream per test case? +    .TEST_LPP           (100                   ), // Lines per packet +    .TEST_MIN_INJ_RATE  (60                    ), // Minimum injection rate to test +    .TEST_MAX_INJ_RATE  (100                   ), // Maximum injection rate to test +    .TEST_INJ_RATE_INCR (10                    ), // Injection rate increment +    .TEST_GEN_LL_FILES  (0                     )  // Generate files to produce load-latency graphs? +  ) impl ( +    /* no IO */ +  ); +endmodule diff --git a/fpga/usrp3/lib/rfnoc/crossbar/crossbar_tb/chdr_traffic_sink_sim.sv b/fpga/usrp3/lib/rfnoc/crossbar/crossbar_tb/chdr_traffic_sink_sim.sv new file mode 100644 index 000000000..a9fe3ba27 --- /dev/null +++ b/fpga/usrp3/lib/rfnoc/crossbar/crossbar_tb/chdr_traffic_sink_sim.sv @@ -0,0 +1,150 @@ +// +// Copyright 2018 Ettus Research, A National Instruments Company +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// +// Module: chdr_traffic_sink_sim +// Description: +//   A sink for CHDR traffic. Simulation only. +//   Accepts packets and computes the following metrics: +//   - Data integrity errors +//   - Packet latency +//   - Throughput counts +//   All metrics can optionally be written to a file to +//   generate load-latency graphs. + +`timescale 1ns/1ps + +`include "sim_cvita_lib.svh" + +module chdr_traffic_sink_sim #( +  parameter        WIDTH     = 64, +  parameter        MTU       = 5, +  parameter [15:0] NODE_ID   = 'd0, +  parameter [15:0] NUM_NODES = 'd16, +  parameter        FILE_PATH = ".", +  parameter        FLUSH_N   = 4 +) ( +  // Clocks and resets +  input             clk, +  input             rst, +  // Settings        +  input [63:0]      current_time, +  input             start_stb, +  input [7:0]       injection_rate, +  input [15:0]      lines_per_pkt, +  input [7:0]       traffic_patt, +  // CHDR master interface +  input [WIDTH-1:0] s_axis_tdata, +  input             s_axis_tlast, +  input             s_axis_tvalid, +  output            s_axis_tready, +  // Metrics         +  output            session_active, +  output [31:0]     xfer_count, +  output [31:0]     pkt_count, +  output [31:0]     data_err_count, +  output [31:0]     route_err_count +); + +  // Constants +  localparam integer ERR_BIT_PKT_SIZE_MISMATCH = 1; +  localparam integer ERR_BIT_PKT_DATA_MISMATCH = 2; +  localparam integer ERR_BIT_PKT_DEST_MISMATCH = 4; +  localparam integer ERR_BIT_PKT_SEQUENCE_ERR  = 8; + +  cvita_slave  #(.DWIDTH(WIDTH)) s_chdr (.clk(clk)); +  cvita_pkt_t  pkt; + +  assign s_chdr.axis.tdata = s_axis_tdata; +  assign s_chdr.axis.tlast = s_axis_tlast; +  assign s_chdr.axis.tvalid = s_axis_tvalid; +  assign s_axis_tready = s_chdr.axis.tready; + +  logic         running = 0; +  integer       num_data_errs = 0; +  integer       num_route_errs = 0; +  logic [31:0]  num_pkts_xferd = 0; +  logic [31:0]  num_samps_xferd = 0; + +  assign data_err_count = num_data_errs; +  assign route_err_count = num_route_errs; +  assign xfer_count = num_samps_xferd; +  assign pkt_count = num_pkts_xferd; +  assign session_active = running; + +  integer session = 0; +  string filename; +  integer handle = 0; +  integer err = 0; +  integer bus_idle_cnt = 0; +  logic [WIDTH-1:0] i; + +  // Egress buff in source is MTU + 4 +  localparam integer IDLE_TIMEOUT = (1 << (MTU + 4 + FLUSH_N)); + +  initial begin: consume_blk +    // Consume infinitely +    s_chdr.reset(); +    while (1) begin +      // A session begins on the posedge of start_stb +      while (~start_stb) @(posedge clk); +      session = session + 1; +      $sformat(filename, "%s/pkts_node%05d_inj%03d_lpp%05d_traffic%c_sess%04d.csv", +        FILE_PATH, NODE_ID, injection_rate, lines_per_pkt, traffic_patt, session); +      if (FILE_PATH != "") begin +        handle = $fopen(filename, "w"); +        if (handle == 0) begin +          $error("Could not open file: %s", filename); +          $finish(); +        end +      end +      if (handle != 0) $fdisplay(handle, "Src,Dst,Seqno,Error,Latency"); +      s_chdr.reset(); +      num_data_errs = 0; +      num_route_errs = 0; +      num_pkts_xferd = 0; +      num_samps_xferd = 0; +      bus_idle_cnt = 0; +      running = 1; +      while (1) begin +        // Pull packet from bus +        err = 0; +        if (~s_chdr.axis.tvalid[0]) begin +          @(posedge clk); +          bus_idle_cnt = bus_idle_cnt + 1; +          if (bus_idle_cnt <= IDLE_TIMEOUT) +            continue; +          else +            break; +        end +        s_chdr.pull_pkt(pkt, 0); +        bus_idle_cnt = 0; +        num_pkts_xferd = num_pkts_xferd + 1; +        num_samps_xferd = num_samps_xferd + lines_per_pkt; +        // Validate packet +        if (pkt.hdr.dst_sid != NODE_ID) begin +          err = err + ERR_BIT_PKT_DEST_MISMATCH; +          num_route_errs = num_route_errs + 1; +        end +        if (pkt.payload.size() != lines_per_pkt-2) begin +          err = err + ERR_BIT_PKT_SIZE_MISMATCH; +          num_data_errs = num_data_errs + 1; +        end else begin +          for (i = 'd0; i < (lines_per_pkt-2); i=i+1) begin +            if (pkt.payload[i] != i) begin +              err = err + ERR_BIT_PKT_DATA_MISMATCH; +              num_data_errs = num_data_errs + 1; +              break; +            end +          end +        end +        if (handle != 0) $fdisplay(handle, "%00d,%00d,%00d,%00d,%00d", +          pkt.hdr.src_sid, pkt.hdr.dst_sid, pkt.hdr.seqnum, err, (current_time - pkt.hdr.timestamp)); +      end +      running = 0; +      if (handle != 0) $fclose(handle); +    end +  end + +endmodule
\ No newline at end of file diff --git a/fpga/usrp3/lib/rfnoc/crossbar/crossbar_tb/chdr_traffic_source_sim.sv b/fpga/usrp3/lib/rfnoc/crossbar/crossbar_tb/chdr_traffic_source_sim.sv new file mode 100644 index 000000000..8c3d974c9 --- /dev/null +++ b/fpga/usrp3/lib/rfnoc/crossbar/crossbar_tb/chdr_traffic_source_sim.sv @@ -0,0 +1,202 @@ +// +// Copyright 2018 Ettus Research, A National Instruments Company +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// +// Module: chdr_traffic_source_sim +// Description: +//   A traffic generator for CHDR traffic. Simulation only. +//   Supports multiple traffic pattern and injection rates. +// + +`timescale 1ns/1ps + +`include "sim_cvita_lib.svh" + +module chdr_traffic_source_sim #( +  parameter        WIDTH        = 64,   // Width of the AXI-Stream data bus +  parameter        MTU          = 5,    // log2 of the max number of lines in a packet +  parameter [15:0] NODE_ID      = 'd0,  // Node ID for this generator +  parameter [15:0] NUM_NODES    = 'd16  // Total number of generators in the application +) ( +  // Clocks and resets +  input               clk,              // AXI-Stream clock +  input               rst,              // AXI-Stream reset +  // Settings          +  input [63:0]        current_time,     // The current value of the global timebase (synch to clk) +  input               start_stb,        // A strobe that indicates the start of a generation session +  input [7:0]         injection_rate,   // The inject rate (in percent) to simulate +  input [15:0]        lines_per_pkt,    // Number of lines per packet to generate +  input [7:0]         traffic_patt,     // The traffic pattern (see localparams below for values) +  input [31:0]        num_pkts_to_send, // Number of packets to send +  // CHDR master interface +  output [WIDTH-1:0]  m_axis_tdata,     // AXI-Stream master tdata +  output              m_axis_tlast,     // AXI-Stream master tlast +  output              m_axis_tvalid,    // AXI-Stream master tvalid +  input               m_axis_tready,    // AXI-Stream master tready +  // Metrics           +  output              session_active,   // Signal indicating if generation session is active +  output [63:0]       session_duration, // Session duration (only valid after session ends) +  output [31:0]       xfer_count,       // Number of lines transferred (only valid after session ends) +  output [31:0]       pkt_count         // Number of packets transferred (only valid after session ends) +); +  // **** Supported Traffic Patters **** +  localparam [7:0] TRAFFIC_PATT_LOOPBACK       = 8'd76;  //L +  localparam [7:0] TRAFFIC_PATT_NEIGHBOR       = 8'd78;  //N +  localparam [7:0] TRAFFIC_PATT_BIT_COMPLEMENT = 8'd67;  //C +  localparam [7:0] TRAFFIC_PATT_SEQUENTIAL     = 8'd83;  //S +  localparam [7:0] TRAFFIC_PATT_UNIFORM        = 8'd85;  //U +  localparam [7:0] TRAFFIC_PATT_UNIFORM_OTHERS = 8'd79;  //O +  localparam [7:0] TRAFFIC_PATT_RANDOM_PERM    = 8'd82;  //R + +  cvita_master  #(.DWIDTH(WIDTH)) m_chdr (.clk(clk)); +  axis_t        #(.DWIDTH(WIDTH)) post_fifo (.clk(clk)); +  axis_t        #(.DWIDTH(WIDTH)) pre_gate (.clk(clk)); +  cvita_hdr_t  header; +  reg throttle = 1'b1; + +  logic        running = 0; +  logic [31:0] curr_pkt_num = 'd0; +  logic [31:0] num_samps_xferd = 'd0; +  logic [63:0] start_time = 0; +  logic [63:0] stop_time = 0; +  logic [15:0] last_gen_sid = (NODE_ID - 16'd1); + +  assign xfer_count = num_samps_xferd; +  assign pkt_count = curr_pkt_num; +  assign session_duration = (stop_time - start_time); +  assign session_active = running; + +  // Utility function to assign SIDs based on traffic pattern +  function [15:0] gen_dst_sid; +    input [7:0] traffic_patt; +    input [15:0] last_sid; + +    if (traffic_patt == TRAFFIC_PATT_UNIFORM) begin +      gen_dst_sid = $urandom_range('d0, NUM_NODES-'d1); +    end else if (traffic_patt == TRAFFIC_PATT_UNIFORM_OTHERS) begin +      logic [31:0] rnum = $urandom_range('d0, NUM_NODES-'d2); +      if (rnum < NODE_ID) +        gen_dst_sid = rnum[15:0]; +      else +        gen_dst_sid = rnum[15:0] + 16'd1; +    end else if (traffic_patt == TRAFFIC_PATT_SEQUENTIAL) begin +      gen_dst_sid = (last_sid + 16'd1) % NUM_NODES; +    end else if (traffic_patt == TRAFFIC_PATT_NEIGHBOR) begin +      gen_dst_sid = (NODE_ID + 16'd1) % NUM_NODES; +    end else if (traffic_patt == TRAFFIC_PATT_LOOPBACK) begin +      gen_dst_sid = NODE_ID; +    end else if (traffic_patt == TRAFFIC_PATT_BIT_COMPLEMENT) begin +      gen_dst_sid = (NUM_NODES - NODE_ID - 1) % NUM_NODES; +    end else if (traffic_patt == TRAFFIC_PATT_RANDOM_PERM) begin +      //TODO: Implement me +      gen_dst_sid = 0; +    end else begin +      gen_dst_sid = 'd0; +    end +  endfunction + +  // Generation loop. Push to m_chdr infinitely fast +  initial begin: gen_blk +    // Generate infinitely +    $srandom(NODE_ID + NUM_NODES); +    m_chdr.reset(); +    while (1) begin +      // A generation session begins on the posedge of start_stb +      while (~start_stb) @(posedge clk); +      curr_pkt_num = 'd0; +      m_chdr.reset(); +      num_samps_xferd = 'd0; +      start_time = current_time; +      running = 1; +      while (curr_pkt_num < num_pkts_to_send) begin +        header = '{ +          pkt_type:DATA, has_time:1, eob:0, +          seqnum:curr_pkt_num[11:0], length:(lines_per_pkt*8), +          src_sid:NODE_ID, dst_sid:gen_dst_sid(traffic_patt, last_gen_sid), +          timestamp:0 //TS attached later +        }; +        last_gen_sid = header.dst_sid; +        curr_pkt_num = curr_pkt_num + 'd1; +        m_chdr.push_ramp_pkt(lines_per_pkt-2, 'h0, 'h1, header); +        num_samps_xferd = num_samps_xferd + lines_per_pkt; +      end +      running = 0; +      stop_time = current_time; +    end +  end + +  // Capture packets in a really short FIFO (for backpressure) +  axi_fifo #( +    .WIDTH(WIDTH+1), .SIZE(MTU + 1) +  ) fifo_i ( +    .clk      (clk),  +    .reset    (rst),  +    .clear    (1'b0), +    .i_tdata  ({m_chdr.axis.tlast, m_chdr.axis.tdata}), +    .i_tvalid (m_chdr.axis.tvalid), +    .i_tready (m_chdr.axis.tready), +    .o_tdata  ({post_fifo.tlast, post_fifo.tdata}), +    .o_tvalid (post_fifo.tvalid), +    .o_tready (post_fifo.tready), +    .space    (), +    .occupied () +  ); + +  // Attach timestamp after the packet leaves the FIFO after +  // throttling.  + +  localparam [1:0] ST_HDR   = 2'd0; +  localparam [1:0] ST_TS    = 2'd1; +  localparam [1:0] ST_BODY  = 2'd2; + +  reg [1:0] pkt_state = ST_HDR; +  always_ff @(posedge clk) begin +    if (rst) begin +      pkt_state <= ST_HDR; +    end else if (pre_gate.tvalid & pre_gate.tready) begin +      case (pkt_state) +        ST_HDR: +          if (~pre_gate.tlast) +            pkt_state <= pre_gate.tdata[61] ? ST_TS : ST_BODY; +        ST_TS: +          pkt_state <= pre_gate.tlast ? ST_HDR : ST_BODY; +        ST_BODY: +          pkt_state <= pre_gate.tlast ? ST_HDR : ST_BODY; +        default: +          pkt_state <= ST_HDR; +      endcase +    end +  end + +  // Enforce injection rate by pulling from FIFO with a certain time probability +  always_ff @(posedge clk) begin +    throttle <= ($urandom_range(32'd99, 32'd0) > {24'h0, injection_rate}); +  end + +  // Insert timestamp + throttle logic +  assign pre_gate.tdata   = (pkt_state == ST_TS) ? current_time : post_fifo.tdata; +  assign pre_gate.tlast   = post_fifo.tlast; +  assign pre_gate.tvalid  = post_fifo.tvalid & ~throttle; +  assign post_fifo.tready = pre_gate.tready & ~throttle; + +  // Gate the packet to smooth out throttle-related noise. +  // This also serves as a buffer for the packet in case things are backed up +  axi_packet_gate #( +    .WIDTH(WIDTH), .SIZE(MTU + 4), .USE_AS_BUFF(1) +  ) pkt_gate_i ( +    .clk      (clk),  +    .reset    (rst),  +    .clear    (1'b0), +    .i_tdata  (pre_gate.tdata), +    .i_tlast  (pre_gate.tlast), +    .i_terror (1'b0), +    .i_tvalid (pre_gate.tvalid), +    .i_tready (pre_gate.tready), +    .o_tdata  (m_axis_tdata), +    .o_tlast  (m_axis_tlast), +    .o_tvalid (m_axis_tvalid), +    .o_tready (m_axis_tready) +  ); + +endmodule
\ No newline at end of file diff --git a/fpga/usrp3/lib/rfnoc/crossbar/crossbar_tb/crossbar_tb.sv b/fpga/usrp3/lib/rfnoc/crossbar/crossbar_tb/crossbar_tb.sv new file mode 100644 index 000000000..fc9d53fe7 --- /dev/null +++ b/fpga/usrp3/lib/rfnoc/crossbar/crossbar_tb/crossbar_tb.sv @@ -0,0 +1,428 @@ +// +// Copyright 2018 Ettus Research, A National Instruments Company +// +// SPDX-License-Identifier: LGPL-3.0-or-later + + +`timescale 1ns/1ps +`define NS_PER_TICK 1 +`define NUM_TEST_CASES 7 + +`include "sim_clks_rsts.vh" +`include "sim_exec_report.vh" +`include "sim_set_rb_lib.svh" +`include "sim_axis_lib.svh" + +`define SIM_TIMEOUT_US 1000000 // Default: 1s + +module crossbar_tb #( +  parameter TEST_NAME          = "crossbar_tb", +  // Router parameters +  parameter ROUTER_IMPL        = "axi_crossbar", // Router implementation +  parameter ROUTER_PORTS       = 10,             // # Router ports +  parameter ROUTER_DWIDTH      = 64,             // Router datapath width +  parameter MTU_LOG2           = 7,              // log2 of max packet size for router +  parameter NUM_MASTERS        = ROUTER_PORTS,   // Number of data generators in test +  // Test parameters +  parameter TEST_MAX_PACKETS   = 50,             // How many packets to stream per test case? +  parameter TEST_LPP           = 50,             // Lines per packet +  parameter TEST_MIN_INJ_RATE  = 60,             // Minimum injection rate to test +  parameter TEST_MAX_INJ_RATE  = 100,            // Maximum injection rate to test +  parameter TEST_INJ_RATE_INCR = 10,             // Injection rate increment +  parameter TEST_GEN_LL_FILES  = 0               // Generate files to produce load-latency graphs? + +)( +  /* no IO */ +); +  `TEST_BENCH_INIT(TEST_NAME,`NUM_TEST_CASES,`NS_PER_TICK) + +  //---------------------------------------------------- +  // General test setup +  //---------------------------------------------------- + +  // Clocks and reset +  `DEFINE_CLK(clk, 5.000, 50) +  `DEFINE_RESET(rst, 0, 10) + +  // Timekeeper (cycle counter) +  logic [63:0] timestamp; +  initial begin : timekeeper_blk +    while (rst) @(posedge clk); +    timestamp = 'd0; +    while (~rst) begin +      @(posedge clk); +      timestamp = timestamp + 'd1; +    end +  end + +  //---------------------------------------------------- +  // Instantiate traffic generators, checkers, buses +  //---------------------------------------------------- +  localparam FILE_PATH = {`WORKING_DIR, "/data/", ROUTER_IMPL}; + +  // Data buses +  axis_t #(.DWIDTH(ROUTER_DWIDTH), .NUM_STREAMS(ROUTER_PORTS)) src2rtr_axis (.clk(clk)); +  axis_t #(.DWIDTH(ROUTER_DWIDTH), .NUM_STREAMS(ROUTER_PORTS)) rtr2snk_axis (.clk(clk)); +   +  // Control buses +  settings_bus_master #(.SR_AWIDTH(16), .SR_DWIDTH(32)) rtr_sb (.clk(clk)); +  wire rtr_sb_ack; + +  // Test vector source and sink instantiation +  logic [7:0]   set_injection_rate; +  logic [15:0]  set_lines_per_pkt; +  logic [7:0]   set_traffic_patt; +  logic [31:0]  set_num_pkts_to_send; +  logic         snk_start_stb = 0; +  logic         src_start_stb = 0; + +  wire [63:0]             session_duration   [0:ROUTER_PORTS-1]; +  wire [ROUTER_PORTS-1:0] src_active; +  wire [31:0]             src_xfer_count     [0:ROUTER_PORTS-1]; +  wire [31:0]             src_pkt_count      [0:ROUTER_PORTS-1]; +  wire [ROUTER_PORTS-1:0] snk_active; +  wire [31:0]             snk_xfer_count     [0:ROUTER_PORTS-1]; +  wire [31:0]             snk_pkt_count      [0:ROUTER_PORTS-1]; +  wire [31:0]             snk_data_err_count [0:ROUTER_PORTS-1]; +  wire [31:0]             snk_route_err_count[0:ROUTER_PORTS-1]; + +  wire  deadlock_detected; +  reg   deadlock_detected_del = 1'b0; +  always @(posedge clk) deadlock_detected_del <= deadlock_detected; +  wire  deadlock_re = (deadlock_detected & ~deadlock_detected_del); +  wire  deadlock_fe = (~deadlock_detected & deadlock_detected_del); + +  genvar i; +  generate for (i = 0; i < ROUTER_PORTS; i=i+1) begin: src_snk_blk +    chdr_traffic_source_sim #( +      .WIDTH            (ROUTER_DWIDTH), +      .MTU              (MTU_LOG2), +      .NODE_ID          (i), +      .NUM_NODES        (ROUTER_PORTS) +    ) traffic_src (      +      .clk              (clk), +      .rst              (rst), +      .current_time     (timestamp), +      .start_stb        (src_start_stb & (i < NUM_MASTERS)), +      .injection_rate   (set_injection_rate), +      .lines_per_pkt    (set_lines_per_pkt), +      .traffic_patt     (set_traffic_patt), +      .num_pkts_to_send (set_num_pkts_to_send), +      .m_axis_tdata     (src2rtr_axis.tdata[((i+1)*ROUTER_DWIDTH)-1:i*ROUTER_DWIDTH]), +      .m_axis_tlast     (src2rtr_axis.tlast[i]), +      .m_axis_tvalid    (src2rtr_axis.tvalid[i]), +      .m_axis_tready    (src2rtr_axis.tready[i]), +      .session_active   (src_active[i]), +      .session_duration (session_duration[i]), +      .xfer_count       (src_xfer_count[i]), +      .pkt_count        (src_pkt_count[i]) +    ); +   +    chdr_traffic_sink_sim #( +      .WIDTH            (ROUTER_DWIDTH), +      .MTU              (MTU_LOG2), +      .NODE_ID          (i), +      .NUM_NODES        (ROUTER_PORTS), +      .FILE_PATH        (TEST_GEN_LL_FILES==1 ? FILE_PATH : "") +    ) traffic_sink (     +      .clk              (clk), +      .rst              (rst), +      .current_time     (timestamp), +      .start_stb        (snk_start_stb), +      .injection_rate   (set_injection_rate), +      .lines_per_pkt    (set_lines_per_pkt), +      .traffic_patt     (set_traffic_patt), +      .s_axis_tdata     (rtr2snk_axis.tdata[((i+1)*ROUTER_DWIDTH)-1:i*ROUTER_DWIDTH]), +      .s_axis_tlast     (rtr2snk_axis.tlast[i]), +      .s_axis_tvalid    (rtr2snk_axis.tvalid[i]), +      .s_axis_tready    (rtr2snk_axis.tready[i]), +      .session_active   (snk_active[i]), +      .xfer_count       (snk_xfer_count[i]), +      .pkt_count        (snk_pkt_count[i]), +      .data_err_count   (snk_data_err_count[i]), +      .route_err_count  (snk_route_err_count[i]) +    ); +  end endgenerate + +  //---------------------------------------------------- +  // Instantiate DUT +  //---------------------------------------------------- +  generate if (ROUTER_IMPL == "FIFO") begin +    for (i = 0; i < ROUTER_PORTS; i=i+1) begin +      axi_fifo #( +        .WIDTH(ROUTER_DWIDTH+1), .SIZE(0) +      ) fifo_i ( +        .clk      (clk),  +        .reset    (rst),  +        .clear    (1'b0), +        .i_tdata  ({src2rtr_axis.tlast[i], src2rtr_axis.tdata[((i+1)*ROUTER_DWIDTH)-1:i*ROUTER_DWIDTH]}), +        .i_tvalid (src2rtr_axis.tvalid[i]), +        .i_tready (src2rtr_axis.tready[i]), +        .o_tdata  ({rtr2snk_axis.tlast[i], rtr2snk_axis.tdata[((i+1)*ROUTER_DWIDTH)-1:i*ROUTER_DWIDTH]}), +        .o_tvalid (rtr2snk_axis.tvalid[i]), +        .o_tready (rtr2snk_axis.tready[i]), +        .space    (), +        .occupied () +      ); +    end +  end else if (ROUTER_IMPL == "axi_crossbar") begin +    axi_crossbar #( +      .BASE         (0), +      .FIFO_WIDTH   (ROUTER_DWIDTH), +      .DST_WIDTH    (16), +      .NUM_INPUTS   (ROUTER_PORTS), +      .NUM_OUTPUTS  (ROUTER_PORTS) +    ) router_dut_i ( +       // General +      .clk          (clk), +      .reset        (rst), +      .clear        (1'b0), +      .local_addr   (8'd0), +      // Inputs      +      .i_tdata      (src2rtr_axis.tdata), +      .i_tlast      (src2rtr_axis.tlast), +      .i_tvalid     (src2rtr_axis.tvalid), +      .i_tready     (src2rtr_axis.tready), +      .pkt_present  (src2rtr_axis.tvalid), +      // Output +      .o_tdata      (rtr2snk_axis.tdata), +      .o_tlast      (rtr2snk_axis.tlast), +      .o_tvalid     (rtr2snk_axis.tvalid), +      .o_tready     (rtr2snk_axis.tready), +      // Setting Bus +      .set_stb      (rtr_sb.settings_bus.set_stb), +      .set_addr     (rtr_sb.settings_bus.set_addr), +      .set_data     (rtr_sb.settings_bus.set_data), +      // Readback bus +      .rb_rd_stb    (1'b0), +      .rb_addr      ({(2*$clog2(ROUTER_PORTS)){1'b0}}), +      .rb_data      () +    ); +  end else if (ROUTER_IMPL == "chdr_crossbar_nxn") begin +    chdr_crossbar_nxn #( +      .CHDR_W         (ROUTER_DWIDTH), +      .NPORTS         (ROUTER_PORTS), +      .DEFAULT_PORT   (0), +      .MTU            (MTU_LOG2), +      .ROUTE_TBL_SIZE (6), +      .MUX_ALLOC      ("ROUND-ROBIN"), +      .OPTIMIZE       ("AREA"), +      .NPORTS_MGMT    (0), +      .EXT_RTCFG_PORT (1) +    ) router_dut_i ( +       // General          +      .clk            (clk), +      .reset          (rst), +      // Inputs +      .s_axis_tdata   (src2rtr_axis.tdata), +      .s_axis_tlast   (src2rtr_axis.tlast), +      .s_axis_tvalid  (src2rtr_axis.tvalid), +      .s_axis_tready  (src2rtr_axis.tready), +      // Output +      .m_axis_tdata   (rtr2snk_axis.tdata), +      .m_axis_tlast   (rtr2snk_axis.tlast), +      .m_axis_tvalid  (rtr2snk_axis.tvalid), +      .m_axis_tready  (rtr2snk_axis.tready), +      // External router config +      .ext_rtcfg_stb  (rtr_sb.settings_bus.set_stb), +      .ext_rtcfg_addr (rtr_sb.settings_bus.set_addr), +      .ext_rtcfg_data (rtr_sb.settings_bus.set_data), +      .ext_rtcfg_ack  (rtr_sb_ack) +    ); +  end else begin +    axis_ctrl_crossbar_nxn #( +      .WIDTH            (ROUTER_DWIDTH), +      .NPORTS           (ROUTER_PORTS), +      .TOPOLOGY         (ROUTER_IMPL == "axis_ctrl_2d_torus" ? "TORUS" : "MESH"), +      .INGRESS_BUFF_SIZE(MTU_LOG2), +      .ROUTER_BUFF_SIZE (MTU_LOG2), +      .ROUTING_ALLOC    ("WORMHOLE"), +      .SWITCH_ALLOC     ("PRIO") +    ) router_dut_i ( +       // General +      .clk              (clk), +      .reset            (rst), +      // Inputs          +      .s_axis_tdata     (src2rtr_axis.tdata), +      .s_axis_tlast     (src2rtr_axis.tlast), +      .s_axis_tvalid    (src2rtr_axis.tvalid), +      .s_axis_tready    (src2rtr_axis.tready), +      // Output          +      .m_axis_tdata     (rtr2snk_axis.tdata), +      .m_axis_tlast     (rtr2snk_axis.tlast), +      .m_axis_tvalid    (rtr2snk_axis.tvalid), +      .m_axis_tready    (rtr2snk_axis.tready), +      // Deadlock detection +      .deadlock_detected(deadlock_detected) +    ); +  end endgenerate + +  //---------------------------------------------------- +  // Test routine. Runs tests and writes metrics to file +  //---------------------------------------------------- + +  // Constants +  localparam [7:0] TRAFFIC_PATT_LOOPBACK       = 8'd76;  //L +  localparam [7:0] TRAFFIC_PATT_NEIGHBOR       = 8'd78;  //N +  localparam [7:0] TRAFFIC_PATT_BIT_COMPLEMENT = 8'd67;  //C +  localparam [7:0] TRAFFIC_PATT_SEQUENTIAL     = 8'd83;  //S +  localparam [7:0] TRAFFIC_PATT_UNIFORM        = 8'd85;  //U +  localparam [7:0] TRAFFIC_PATT_UNIFORM_OTHERS = 8'd79;  //O +  localparam [7:0] TRAFFIC_PATT_RANDOM_PERM    = 8'd82;  //R + +  string        filename; +  integer       node; +  integer       session = 0; +  integer       handle = 0; +  logic [63:0]  start_time; +  integer       total_pkts_recvd = 0, total_pkts_sent = 0; + +  task sim_dataflow; +    input [7:0]  injection_rate; +    input [7:0]  traffic_patt; +    input [15:0] lines_per_pkt; +    input [31:0] num_pkts_to_send; +  begin +    session = session + 1; +    $display("--------------- New Simulation ---------------"); +    $display("- Module = %s", ROUTER_IMPL); +    $display("- Nodes = %00d", ROUTER_PORTS); +    $display("- Injection Rate = %00d%%", injection_rate); +    $display("- Traffic Pattern = %c", traffic_patt); +    $display("- Packet Size = %00d words (%00d bits)", lines_per_pkt, ROUTER_DWIDTH); +    $display("- Max Packets = %00d", num_pkts_to_send); +    // Configure settings +    @(posedge clk); +    set_injection_rate = injection_rate; +    set_lines_per_pkt = lines_per_pkt; +    set_traffic_patt = traffic_patt; +    set_num_pkts_to_send = num_pkts_to_send; +    @(posedge clk); +    // Start the sink then the source +    $display("Data flow starting..."); +    snk_start_stb = 1; +    src_start_stb = 1; +    @(posedge clk); +    src_start_stb = 0; +    snk_start_stb = 0; +    @(posedge clk); +    start_time = timestamp; +    // Wait for source blocks to finish generating +    $display("Waiting for packets to transmit... (may take a while)"); +    while (|src_active) begin +      @(posedge clk); +      if (deadlock_re) $display("WARNING: Deadlock detected"); +      if (deadlock_fe) $display("Recovered from deadlock"); +    end  +    // Wait for sink blocks to finish consuming +    $display("All packets transmitted. Waiting to flush..."); +    while (|snk_active) @(posedge clk); +    // If router deadlocks then wait for it to recover +    if (deadlock_detected) begin +      $display("Waiting for deadlock recovery to finish..."); +      while (deadlock_detected) @(posedge clk); +    end +    repeat(set_lines_per_pkt) @(posedge clk); +    // Record summary to file and print to console +    $sformat(filename, "%s/info_inj%03d_lpp%05d_traffic%c_sess%04d.csv", +      FILE_PATH, injection_rate, lines_per_pkt, traffic_patt, session); +    if (TEST_GEN_LL_FILES == 1) begin +      handle = $fopen(filename, "w"); +      if (handle == 0) begin +        $error("Could not open file: %s", filename); +        $finish(); +      end +    end +    if (handle != 0) $fdisplay(handle, "Impl,Node,TxPkts,RxPkts,Duration,ErrRoute,ErrData"); +    total_pkts_sent = 0; +    total_pkts_recvd = 0; +    for (node = 0; node < ROUTER_PORTS; node=node+1) begin +      $display("- Node #%03d: TX = %5d pkts, RX = %5d pkts, Inj Rate = %3d%%. Errs = %5d route, %5d data", +        node,src_pkt_count[node], snk_pkt_count[node], ((src_xfer_count[node]*100)/session_duration[node]), +        snk_route_err_count[node], snk_data_err_count[node]); +      if (handle != 0) $fdisplay(handle, "%s,%00d,%00d,%00d,%00d,%00d,%00d", ROUTER_IMPL, +        node,src_pkt_count[node], snk_pkt_count[node], session_duration[node], +        snk_route_err_count[node], snk_data_err_count[node]); +      total_pkts_sent = total_pkts_sent + src_pkt_count[node]; +      total_pkts_recvd = total_pkts_recvd + snk_pkt_count[node]; +      `ASSERT_ERROR(snk_route_err_count[node] == 0, "Routing errors. Received packets destined to other nodes"); +      `ASSERT_ERROR(snk_data_err_count[node] == 0, "Integrity errors. Received corrupted packets"); +    end +    $display("Finished. Elapsed = %00d cycles, TX = %00d pkts, RX = %00d pkts", +      (timestamp - start_time), total_pkts_sent, total_pkts_recvd); +    `ASSERT_ERROR(total_pkts_recvd == total_pkts_sent, "Total # TX packets did not match the total # RX packets"); +    if (handle != 0) $fclose(handle); +    $display("----------------------------------------------"); +  end +  endtask + +  //---------------------------------------------------- +  // Main test loop +  //---------------------------------------------------- + +  logic [31:0] MAX_PACKETS    = TEST_MAX_PACKETS; +  logic [15:0] LPP            = TEST_LPP; +  integer      MIN_INJ_RATE   = TEST_MIN_INJ_RATE; +  integer      MAX_INJ_RATE   = TEST_MAX_INJ_RATE; +  integer      INJ_RATE_INCR  = TEST_INJ_RATE_INCR; + +  integer inj_rate = 0; +  initial begin : tb_main +    src_start_stb = 0; +    snk_start_stb = 0; +    rtr_sb.reset(); +    while (rst) @(posedge clk); + +    repeat (10) @(posedge clk); + +    `TEST_CASE_START("Set up crossbar"); +      for (node = 0; node < ROUTER_PORTS; node=node+1) begin +        if (ROUTER_IMPL == "axi_crossbar") begin +          rtr_sb.write(16'd256 + node[15:0], {16'h0, node[15:0]}); +        end else if (ROUTER_IMPL == "chdr_crossbar_nxn") begin +          rtr_sb.write(node[15:0], {16'h0, node[15:0]}); +          while (~rtr_sb_ack) @(posedge clk); +        end +      end +    `TEST_CASE_DONE(1) + +     `TEST_CASE_START("Simulate LOOPBACK Traffic Pattern"); +       for (inj_rate = MIN_INJ_RATE; inj_rate <= MAX_INJ_RATE; inj_rate = inj_rate + INJ_RATE_INCR) begin +         sim_dataflow(inj_rate, TRAFFIC_PATT_LOOPBACK, LPP, MAX_PACKETS); +       end +     `TEST_CASE_DONE(1) + +     `TEST_CASE_START("Simulate SEQUENTIAL Traffic Pattern"); +       for (inj_rate = MIN_INJ_RATE; inj_rate <= MAX_INJ_RATE; inj_rate = inj_rate + INJ_RATE_INCR) begin +         sim_dataflow(inj_rate, TRAFFIC_PATT_SEQUENTIAL, LPP, MAX_PACKETS); +       end +     `TEST_CASE_DONE(1) + +     `TEST_CASE_START("Simulate UNIFORM Traffic Pattern"); +       for (inj_rate = MIN_INJ_RATE; inj_rate <= MAX_INJ_RATE; inj_rate = inj_rate + INJ_RATE_INCR) begin +         sim_dataflow(inj_rate, TRAFFIC_PATT_UNIFORM, LPP, MAX_PACKETS); +       end +     `TEST_CASE_DONE(1) + +     `TEST_CASE_START("Simulate UNIFORM_OTHERS Traffic Pattern"); +       for (inj_rate = MIN_INJ_RATE; inj_rate <= MAX_INJ_RATE; inj_rate = inj_rate + INJ_RATE_INCR) begin +         sim_dataflow(inj_rate, TRAFFIC_PATT_UNIFORM_OTHERS, LPP, MAX_PACKETS); +       end +     `TEST_CASE_DONE(1) + +     `TEST_CASE_START("Simulate BIT_COMPLEMENT Traffic Pattern"); +       for (inj_rate = MIN_INJ_RATE; inj_rate <= MAX_INJ_RATE; inj_rate = inj_rate + INJ_RATE_INCR) begin +         sim_dataflow(inj_rate, TRAFFIC_PATT_BIT_COMPLEMENT, LPP, MAX_PACKETS); +       end +     `TEST_CASE_DONE(1) + +    `TEST_CASE_START("Simulate NEIGHBOR Traffic Pattern"); +      for (inj_rate = MIN_INJ_RATE; inj_rate <= MAX_INJ_RATE; inj_rate = inj_rate + INJ_RATE_INCR) begin +        sim_dataflow(inj_rate, TRAFFIC_PATT_NEIGHBOR, LPP, MAX_PACKETS); +      end +    `TEST_CASE_DONE(1) + +    `TEST_BENCH_DONE +  end // initial begin + +endmodule diff --git a/fpga/usrp3/lib/rfnoc/crossbar/crossbar_tb/gen_load_latency_graph.py b/fpga/usrp3/lib/rfnoc/crossbar/crossbar_tb/gen_load_latency_graph.py new file mode 100755 index 000000000..35821c2c4 --- /dev/null +++ b/fpga/usrp3/lib/rfnoc/crossbar/crossbar_tb/gen_load_latency_graph.py @@ -0,0 +1,169 @@ +#!/usr/bin/env python3 +# +# Copyright 2018 Ettus Research, A National Instruments Company +# +# SPDX-License-Identifier: LGPL-3.0-or-later +# +# Description +#   Parses the output files generated by crossbar_tb and outputs +#   a load-latency graph and a expected-actual throughput graph + +import os, sys +import argparse +import time +import glob +import csv +import re +import numpy as np + +import matplotlib +#matplotlib.use('Agg') +import matplotlib.pyplot as plt + +def get_options(): +    parser = argparse.ArgumentParser(description='Generate Load Latency Graphs') +    parser.add_argument('datadir', type=str, default='.', help='Location of packet capture files generated by testbench') +    return parser.parse_args() + +TRAFFIC_PATTERNS = {'U':'UNIFORM', 'O':'UNIFORM_OTHERS', 'N':'NEIGHBOR', 'L':'LOOPBACK', 'S':'SEQUENTIAL', 'C':'BIT_COMPLEMENT', 'R':'RANDOM_PERM'} + +class InfoFile(): +    def __init__(self, filename): +        # Extract test info from filename +        m = re.search(r".*/info_inj([0-9]+)_lpp([0-9]+)_traffic(.)_sess([0-9]+)\.csv", filename) +        if m is None: +            raise ValueError('Incorrect filename format: %s'%(filename)) +        self.inj_rate = int(m.group(1)) +        self.lpp = int(m.group(2)) +        self.traffic_patt = TRAFFIC_PATTERNS[m.group(3)] +        self.session = int(m.group(4)) + +        self.tx_pkts = 0 +        self.rx_pkts = 0 +        self.duration = 0 +        self.errs = 0 +        self.nodes = 0 +        with open(filename, 'r') as csvfile: +            reader = csv.reader(csvfile, delimiter=',') +            isheader = True +            for row in reader: +                if isheader: +                    isheader = False +                    if row != ['Impl', 'Node', 'TxPkts', 'RxPkts', 'Duration', 'ErrRoute', 'ErrData']: +                        raise ValueError('Incorrect header: %s'%(filename)) +                else: +                    self.impl = row[0] +                    self.tx_pkts = self.tx_pkts + int(row[2]) +                    self.rx_pkts = self.tx_pkts + int(row[3]) +                    self.duration = self.duration + int(row[4]) +                    self.errs = self.errs + int(row[5]) + int(row[6]) +                    self.nodes = self.nodes + 1 +        self.real_inj_rate = (100.0 * self.tx_pkts * self.lpp) / self.duration + +class PktFile(): +    def __init__(self, filename): +        # Extract test info from filename +        m = re.search(r".*/pkts_node([0-9]+)_inj([0-9]+)_lpp([0-9]+)_traffic(.)_sess([0-9]+)\.csv", filename) +        if m is None: +            raise ValueError('Incorrect filename format: %s'%(filename)) +        self.node = int(m.group(1)) +        self.inj_rate = int(m.group(2)) +        self.lpp = int(m.group(3)) +        self.traffic_patt = TRAFFIC_PATTERNS[m.group(4)] +        self.session = int(m.group(5)) + +        self.latencies = [] +        with open(filename, 'r') as csvfile: +            reader = csv.reader(csvfile, delimiter=',') +            isheader = True +            for row in reader: +                if isheader: +                    isheader = False +                    if row != ['Src', 'Dst', 'Seqno', 'Error', 'Latency']: +                        raise ValueError('Incorrect header: %s'%(filename)) +                else: +                    self.latencies.append(int(row[4])) + + +######################################################################## +# main +######################################################################## +if __name__=='__main__': +    options = get_options() + +    if (not os.path.isdir(options.datadir)): +        print('ERROR: Data director %s does not exist'%(options.datadir)) +        sys.exit(1) + +    info_db = dict() +    info_files = glob.glob(os.path.join(options.datadir, 'info*.csv')) +    router_impl = '' +    lines_per_pkt = 0 +    for ifile in info_files: +        print('INFO: Reading %s...'%(ifile)) +        tmp = InfoFile(ifile) +        router_impl = tmp.impl  # Assume that all files have the same impl +        lines_per_pkt = tmp.lpp  # Assume that all files have the same LPP +        info_db[(tmp.lpp, tmp.traffic_patt, tmp.inj_rate)] = tmp + +    pkt_db = dict() +    pkts_files = glob.glob(os.path.join(options.datadir, 'pkts*.csv')) +    for pfile in pkts_files: +        print('INFO: Reading %s...'%(pfile)) +        tmp = PktFile(pfile) +        config_key = (tmp.lpp, tmp.traffic_patt) +        if config_key not in pkt_db: +            pkt_db[config_key] = dict() +        if tmp.inj_rate not in pkt_db[config_key]: +            pkt_db[config_key][tmp.inj_rate] = [] + + +        pkt_db[config_key][tmp.inj_rate].extend(tmp.latencies) + +    # Write load-latency plots to file +    actual_inj_rate_db = dict() +    for config in sorted(pkt_db): +        (lpp, traffic_patt) = config +        ll_file = 'load-latency_%s_traffic-%s_lpp-%d.png'%(router_impl, traffic_patt, lpp) +        print('INFO: Writing file ' + ll_file + '...') +        percentile = [0, 25, 50, 75, 90, 95, 99, 99.9, 100] +        plt.figure() +        plt.title('Load Latency Graph for %s\n(Traffic: %s, LPP: %d)'%(router_impl, traffic_patt, lpp)) +        for p in percentile: +            plot_data = dict() +            for inj_rate in pkt_db[config]: +                real_inj_rate = info_db[(lpp, traffic_patt, inj_rate)].real_inj_rate +                plot_data[real_inj_rate] = np.percentile(pkt_db[config][inj_rate], p) +            latencies = [] +            rates = [] +            for inj_rate in sorted(plot_data): +                rates.append(inj_rate) +                latencies.append(plot_data[inj_rate]) +            plt.plot(rates, latencies, label='$P_{%.1f}$'%(p)) +            plt.xlabel('Load (%)') +            plt.xticks(range(0, 110, 10)) +            plt.ylabel('Latency (cycles)') +            plt.grid(True) +        plt.legend() +        plt.savefig(os.path.join(options.datadir, ll_file), dpi=120) +        # Generate actual inj_rate graph +        real_inj_rates = [] +        for inj_rate in sorted(pkt_db[config]): +            real_inj_rates.append(info_db[(lpp, traffic_patt, inj_rate)].real_inj_rate) +        actual_inj_rate_db[config] = (sorted(pkt_db[config]), real_inj_rates) + +    # Write offered vs actual injection rate plots to file +    injrate_file = 'injection-rate_%s_lpp-%d.png'%(router_impl, lines_per_pkt) +    print('INFO: Writing file ' + injrate_file + '...') +    plt.figure() +    plt.title('Max Injection Rate Graph for %s'%(router_impl)) +    for config in actual_inj_rate_db: +        (x, y) = actual_inj_rate_db[config] +        plt.plot(x, y, label=str(config)) +        plt.xlabel('Offered Injection Rate (%)') +        plt.xticks(range(0, 110, 10)) +        plt.ylabel('Accepted Injection Rate (%)') +        plt.yticks(range(0, 110, 10)) +        plt.grid(True) +    plt.legend() +    plt.savefig(os.path.join(options.datadir, injrate_file), dpi=120)
\ No newline at end of file diff --git a/fpga/usrp3/lib/rfnoc/crossbar/crossbar_tb/run_sim_multi.py b/fpga/usrp3/lib/rfnoc/crossbar/crossbar_tb/run_sim_multi.py new file mode 100755 index 000000000..8e546fef9 --- /dev/null +++ b/fpga/usrp3/lib/rfnoc/crossbar/crossbar_tb/run_sim_multi.py @@ -0,0 +1,106 @@ +#!/usr/bin/python3 +# +# Copyright 2018 Ettus Research, a National Instruments Company +# +# SPDX-License-Identifier: LGPL-3.0-or-later +# +# Description +#   Run the crossbar testbench (crossbar_tb) for varios parameter +#   configurations and generates load-latency graphs for each run. + +import argparse +import math +import os, sys +import shutil +import glob +import subprocess + +g_tb_top_template = """ +`timescale 1ns/1ps +module crossbar_tb_auto(); +  crossbar_tb #( +    .TEST_NAME          ("crossbar_tb_auto"), +    .ROUTER_IMPL        ("{rtr_impl}"), +    .ROUTER_PORTS       ({rtr_ports}), +    .ROUTER_DWIDTH      ({rtr_width}), +    .MTU_LOG2           ({rtr_mtu}), +    .NUM_MASTERS        ({rtr_sources}), +    .TEST_MAX_PACKETS   ({tst_maxpkts}), +    .TEST_LPP           ({tst_lpp}), +    .TEST_MIN_INJ_RATE  ({tst_injrate_min}), +    .TEST_MAX_INJ_RATE  ({tst_injrate_max}), +    .TEST_INJ_RATE_INCR (10), +    .TEST_GEN_LL_FILES  (1) +  ) impl ( +    /* no IO */ +  ); +endmodule +""" + +g_test_params = { +    'data': {'rtr_width':64, 'rtr_mtu':7, 'tst_maxpkts':100, 'tst_lpp':100, 'tst_injrate_min':30, 'tst_injrate_max':100}, +    'ctrl': {'rtr_width':64, 'rtr_mtu':5, 'tst_maxpkts':100, 'tst_lpp':20,  'tst_injrate_min':10, 'tst_injrate_max':50}, +} + +g_xb_types = { +    'chdr_crossbar_nxn':'data', 'axi_crossbar':'data', +    'axis_ctrl_2d_torus':'ctrl', 'axis_ctrl_2d_mesh':'ctrl' +} + +def get_options(): +    parser = argparse.ArgumentParser(description='Run correctness sim and generate load-latency plots') +    parser.add_argument('--impl', type=str, default='chdr_crossbar_nxn', help='Implementation (CSV) [%s]'%(','.join(g_xb_types.keys()))) +    parser.add_argument('--ports', type=str, default='16', help='Number of ports (CSV)') +    parser.add_argument('--sources', type=str, default='16', help='Number of active data sources (masters)') +    return parser.parse_args() + +def launch_run(impl, ports, sources): +    run_name = '%s_ports%d_srcs%d'%(impl, ports, sources) +    # Prepare a transform map to autogenerate a TB file +    transform = {'rtr_impl':impl, 'rtr_ports':ports, 'rtr_sources':sources} +    for k,v in g_test_params[g_xb_types[impl]].items(): +        transform[k] = v +    # Create crossbar_tb_auto.sv with specified parameters +    with open('crossbar_tb_auto.sv', 'w') as out_file: +        out_file.write(g_tb_top_template.format(**transform)) +    # Create data directory for the simulation +    data_dir = os.path.join('data', impl) +    export_dir = os.path.join('data', run_name) +    try: +        os.makedirs('data') +    except FileExistsError: +        pass +    os.makedirs(data_dir) +    os.makedirs(export_dir) +    # Run "make xsim" +    exitcode = subprocess.Popen('make xsim TB_TOP_MODULE=crossbar_tb_auto', shell=True).wait() +    if exitcode != 0: +        raise RuntimeError('Error running "make xsim". Was setupenv.sh run?') +    # Generate load-latency graphs +    exitcode = subprocess.Popen('gen_load_latency_graph.py ' + data_dir, shell=True).wait() +    if exitcode != 0: +        raise RuntimeError('Error running "gen_load_latency_graph.py"') +    # Copy files +    os.rename('xsim.log', os.path.join(export_dir, 'xsim.log')) +    for file in glob.glob(os.path.join(data_dir, '*.png')): +        shutil.copy(file, export_dir) +    # Cleanup outputs +    subprocess.Popen('make cleanall', shell=True).wait() +    try: +        os.remove('crossbar_tb_auto.sv') +    except FileNotFoundError: +        pass +    try: +        shutil.rmtree(data_dir) +    except OSError: +        print('WARNING: Could not delete ' + data_dir) + +def main(): +    args = get_options(); +    for impl in args.impl.strip().split(','): +        for ports in args.ports.strip().split(','): +            for sources in args.sources.strip().split(','): +                launch_run(impl, int(ports), min(int(ports), int(sources))) + +if __name__ == '__main__': +    main() diff --git a/fpga/usrp3/lib/rfnoc/crossbar/gen_node_to_coord_mapping.py b/fpga/usrp3/lib/rfnoc/crossbar/gen_node_to_coord_mapping.py new file mode 100755 index 000000000..a2eaf71fb --- /dev/null +++ b/fpga/usrp3/lib/rfnoc/crossbar/gen_node_to_coord_mapping.py @@ -0,0 +1,125 @@ +#!/usr/bin/python3 +# +# Copyright 2018 Ettus Research, a National Instruments Company +# +# SPDX-License-Identifier: LGPL-3.0-or-later +# + +import argparse +import math +import sys +import datetime +import random + +# Parse command line options +# ------------------------------------------------ +def get_options(): +    parser = argparse.ArgumentParser(description='Generate a node to coordinate mapping file.') +    parser.add_argument('--pattern', type=str, default='xy', choices=['xy', 'yx', 'spiral', 'random'], help='Node distribution pattern') +    parser.add_argument('--dimsize', type=int, default=4, help='Maximum dimension size') +    parser.add_argument('--seed', type=int, default=None, help='Seed for random permutation generator') +    return parser.parse_args() + +# Pattern Generators +# ------------------------------------------------ +def gen_xy(N): +    nodes = dict() +    for y in range(N): +        for x in range(N): +            nodes[(y*N + x)] = (x, y) +    return nodes + +def gen_yx(N): +    nodes = dict() +    for y in range(N): +        for x in range(N): +            nodes[(x*N + y)] = (x, y) +    return nodes + +def gen_spiral(N): +    nodes = dict() +    x = y = 0 +    dx = 0 +    dy = -1 +    for i in range(N**2): +        if (-N/2 < x <= N/2) and (-N/2 < y <= N/2): +            nodes[i] = (x + int(math.ceil(N/2)) - 1, y + int(math.ceil(N/2)) - 1) +        if x == y or (x < 0 and x == -y) or (x > 0 and x == 1-y): +            dx, dy = -dy, dx +        x, y = x+dx, y+dy +    return nodes + +def gen_random(N): +    nodes = dict() +    rnodes = random.sample(range(N*N), N*N) +    for y in range(N): +        for x in range(N): +            nodes[rnodes[x*N + y]] = (x, y) +    return nodes + +# Source Generators +# ------------------------------------------------ +def layout_nodes(nodes): +    N = int(math.sqrt(len(nodes))) +    #inv_nodes = {v: k for k, v in nodes.iteritems()} +    coords = {v: k for k, v in nodes.items()} +    lines = [] +    for y in range(N): +        line = '' +        for x in range(N): +            line += '%5d'%(coords[(x,y)]) +        lines.append(line) +    return lines + +def gen_vparams(nodes, N, pattern): +    src_lines = [ '\n// DIM_SIZE = %d, PATTERN = %s'%(N,pattern.upper()), '//------------------------------------' ] +    for l in layout_nodes(nodes): +        src_lines.append('// ' + l) +    bitw = math.ceil(math.log2(N)) +    xvals = ','.join(['%d\'d%d'%(bitw,v[0]) for k, v in sorted(nodes.items(), reverse=True)]) +    yvals = ','.join(['%d\'d%d'%(bitw,v[1]) for k, v in sorted(nodes.items(), reverse=True)]) +    xpar = 'localparam [%d:0] XCOORD_DIM_%03d = {%s};'%(bitw*N*N-1, N, xvals) +    ypar = 'localparam [%d:0] YCOORD_DIM_%03d = {%s};'%(bitw*N*N-1, N, yvals) +    src_lines.append(xpar) +    src_lines.append(ypar) +    src_lines.append('') +    return src_lines + +def gen_lookup_func(dim, N): +    src_lines = [ 'function [CLOG2_DIM_SIZE-1:0] node_to_%sdst;'%(dim), '  input [WIDTH-1:0] header;', 'begin'] +    dim_sizes = range(2, N+1) +    for i in dim_sizes: +        node_bitw = math.ceil(math.log2(i*i)) +        dim_bitw = math.ceil(math.log2(i)) +        prefix = '  ' if (i == dim_sizes[0]) else '  else ' +        src_lines.append(prefix + 'if (DIM_SIZE == %d)'%(i)) +        src_lines.append('    node_to_%sdst = %sCOORD_DIM_%03d[%d*header[%d:0] +: %d];'%(dim,dim.upper(),i,dim_bitw,node_bitw-1,dim_bitw)) +    src_lines.append('  else') +    src_lines.append('    node_to_%sdst = {CLOG2_DIM_SIZE{1\'d0}};'%(dim)) +    src_lines.append('end endfunction\n\n') +    return src_lines + +def gen_vheader(dimsize, mapgen, pattern, filename): +    with open(filename, 'w') as vhfile: +        vhfile.write('// Copyright %s Ettus Research, A National Instruments Company\n'%(datetime.datetime.now().year)) +        vhfile.write('// SPDX-License-Identifier: LGPL-3.0-or-later\n') +        vhfile.write('//\n') +        vhfile.write('// Autogenerated file. Do not modify.\n') +        vhfile.write('// $ %s\n'%(' '.join(sys.argv[:]))) +        vhfile.write('\nparameter CLOG2_DIM_SIZE = $clog2(DIM_SIZE); //Vivado workaround\n\n') +        for i in range(2, dimsize+1): +            nodes = mapgen(i) +            N = math.sqrt(len(nodes)) +            vhfile.write('\n'.join(gen_vparams(nodes, N, pattern))) +        vhfile.write('\n\n') +        vhfile.write('\n'.join(gen_lookup_func('x', dimsize))) +        vhfile.write('\n'.join(gen_lookup_func('y', dimsize))) + +def main(): +    args = get_options(); +    random.seed(args.seed) +    generators = {'xy': gen_xy, 'yx': gen_yx, 'spiral':gen_spiral, 'random': gen_random} +    gen_vheader(args.dimsize, generators[args.pattern], args.pattern, 'mesh_node_mapping.vh') + +if __name__ == '__main__': +    main() diff --git a/fpga/usrp3/lib/rfnoc/crossbar/mesh_2d_dor_router_multi_sw.v b/fpga/usrp3/lib/rfnoc/crossbar/mesh_2d_dor_router_multi_sw.v new file mode 100644 index 000000000..e0338347b --- /dev/null +++ b/fpga/usrp3/lib/rfnoc/crossbar/mesh_2d_dor_router_multi_sw.v @@ -0,0 +1,481 @@ +// +// Copyright 2018 Ettus Research, A National Instruments Company +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// +// Module: mesh_2d_dor_router_multi_sw +// Description: +//   Alternate implementation for mesh_2d_dor_router_single_sw with +//   multiple switches for independent paths between inputs and outputs +//   **NOTE**: This module has not been validated + +module mesh_2d_dor_router_multi_sw #( +  parameter                        WIDTH          = 64, +  parameter                        DIM_SIZE       = 4, +  parameter [$clog2(DIM_SIZE)-1:0] XB_ADDR_X      = 0, +  parameter [$clog2(DIM_SIZE)-1:0] XB_ADDR_Y      = 0, +  parameter                        TERM_BUFF_SIZE = 5, +  parameter                        XB_BUFF_SIZE   = 5, +  parameter                        ROUTING_ALLOC  = "WORMHOLE",  // Routing (switching) method {WORMHOLE, CUT-THROUGH} +  parameter                        SWITCH_ALLOC   = "PRIO"       // Switch allocation algorithm {ROUND-ROBIN, PRIO} +) ( +  // Clocks and resets +  input  wire             clk, +  input  wire             reset, + +  // Terminal connections +  input  wire [WIDTH-1:0] s_axis_ter_tdata, +  input  wire             s_axis_ter_tlast, +  input  wire             s_axis_ter_tvalid, +  output wire             s_axis_ter_tready, +  output wire [WIDTH-1:0] m_axis_ter_tdata, +  output wire             m_axis_ter_tlast, +  output wire             m_axis_ter_tvalid, +  input  wire             m_axis_ter_tready, + +  // West inter-router connections +  input  wire [WIDTH-1:0] s_axis_wst_tdata, +  input  wire [0:0]       s_axis_wst_tdest, +  input  wire             s_axis_wst_tlast, +  input  wire             s_axis_wst_tvalid, +  output wire             s_axis_wst_tready, +  output wire [WIDTH-1:0] m_axis_wst_tdata, +  output wire [0:0]       m_axis_wst_tdest, +  output wire             m_axis_wst_tlast, +  output wire             m_axis_wst_tvalid, +  input  wire             m_axis_wst_tready, + +  // East inter-router connections +  input  wire [WIDTH-1:0] s_axis_est_tdata, +  input  wire [0:0]       s_axis_est_tdest, +  input  wire             s_axis_est_tlast, +  input  wire             s_axis_est_tvalid, +  output wire             s_axis_est_tready, +  output wire [WIDTH-1:0] m_axis_est_tdata, +  output wire [0:0]       m_axis_est_tdest, +  output wire             m_axis_est_tlast, +  output wire             m_axis_est_tvalid, +  input  wire             m_axis_est_tready, + +  // North inter-router connections +  input  wire [WIDTH-1:0] s_axis_nor_tdata, +  input  wire [0:0]       s_axis_nor_tdest, +  input  wire             s_axis_nor_tlast, +  input  wire             s_axis_nor_tvalid, +  output wire             s_axis_nor_tready, +  output wire [WIDTH-1:0] m_axis_nor_tdata, +  output wire [0:0]       m_axis_nor_tdest, +  output wire             m_axis_nor_tlast, +  output wire             m_axis_nor_tvalid, +  input  wire             m_axis_nor_tready, + +  // South inter-router connections +  input  wire [WIDTH-1:0] s_axis_sou_tdata, +  input  wire [0:0]       s_axis_sou_tdest, +  input  wire             s_axis_sou_tlast, +  input  wire             s_axis_sou_tvalid, +  output wire             s_axis_sou_tready, +  output wire [WIDTH-1:0] m_axis_sou_tdata, +  output wire [0:0]       m_axis_sou_tdest, +  output wire             m_axis_sou_tlast, +  output wire             m_axis_sou_tvalid, +  input  wire             m_axis_sou_tready +); +  // ------------------------------------------------- +  // Routing functions +  // ------------------------------------------------- +  `include "mesh_node_mapping.vh" + +  function [2:0] term_route; +    input [WIDTH-1:0] header; +    reg [$clog2(DIM_SIZE)-1:0] xdst, ydst; +    reg signed [$clog2(DIM_SIZE):0] xdiff, ydiff; +  begin +    xdst  = node_to_xdst(header); +    ydst  = node_to_ydst(header); +    xdiff = xdst - XB_ADDR_X; +    ydiff = ydst - XB_ADDR_Y; +    // Routing logic +    if (xdst == XB_ADDR_X && ydst == XB_ADDR_Y) begin +      term_route = 3'd0; //TER +    end else if (xdst == XB_ADDR_X) begin +      if (ydiff < 0) +        term_route = 3'd3; //NOR +      else +        term_route = 3'd4; //SOU +    end else begin +      if (xdiff < 0) +        term_route = 3'd1; //WST +      else +        term_route = 3'd2; //EST +    end +  end +  endfunction + +  function [1:0] xdim_route; +    input [WIDTH-1:0] header; +    reg [$clog2(DIM_SIZE)-1:0] xdst, ydst; +    reg signed [$clog2(DIM_SIZE):0] xdiff, ydiff; +  begin +    xdst  = node_to_xdst(header); +    ydst  = node_to_ydst(header); +    xdiff = xdst - XB_ADDR_X; +    ydiff = ydst - XB_ADDR_Y; +    // Routing logic +    if (xdst == XB_ADDR_X && ydst == XB_ADDR_Y) begin +      xdim_route = 2'd0; //TER +    end else if (xdst == XB_ADDR_X) begin +      if (ydiff < 0) +        xdim_route = 2'd2; //NOR +      else +        xdim_route = 2'd3; //SOU +    end else begin +      xdim_route = 2'd1; //Forward +    end +  end +  endfunction + +  function [0:0] ydim_route; +    input [WIDTH-1:0] header; +    reg [$clog2(DIM_SIZE)-1:0] xdst, ydst; +    reg signed [$clog2(DIM_SIZE):0] xdiff, ydiff; +  begin +    xdst  = node_to_xdst(header); +    ydst  = node_to_ydst(header); +    xdiff = xdst - XB_ADDR_X; +    ydiff = ydst - XB_ADDR_Y; +    // Routing logic +    if (xdst == XB_ADDR_X && ydst == XB_ADDR_Y) begin +      ydim_route = 1'd0; //TER +    end else if (xdst == XB_ADDR_X) begin +      ydim_route = 1'd1; //Forward +    end +  end +  endfunction + + +  // ------------------------------------------------- +  // Input buffers +  // ------------------------------------------------- +  wire [WIDTH-1:0] ter_i_tdata; +  wire             ter_i_tlast; +  wire             ter_i_tvalid; +  wire             ter_i_tready; + +  axi_packet_gate #( +    .WIDTH(WIDTH), .SIZE(TERM_BUFF_SIZE) +  ) term_in_pkt_gate_i ( +    .clk      (clk),  +    .reset    (reset),  +    .clear    (1'b0), +    .i_tdata  (s_axis_ter_tdata), +    .i_tlast  (s_axis_ter_tlast), +    .i_tvalid (s_axis_ter_tvalid), +    .i_tready (s_axis_ter_tready), +    .i_terror (1'b0), +    .o_tdata  (ter_i_tdata), +    .o_tlast  (ter_i_tlast), +    .o_tvalid (ter_i_tvalid), +    .o_tready (ter_i_tready) +  ); + +  wire [WIDTH-1:0] wst_i_tdata,  est_i_tdata,  nor_i_tdata,  sou_i_tdata; +  wire             wst_i_tlast,  est_i_tlast,  nor_i_tlast,  sou_i_tlast; +  wire             wst_i_tvalid, est_i_tvalid, nor_i_tvalid, sou_i_tvalid; +  wire             wst_i_tready, est_i_tready, nor_i_tready, sou_i_tready; + +  axis_ingress_vc_buff #( +    .WIDTH(WIDTH), .NUM_VCS(1), +    .SIZE(XB_BUFF_SIZE), +    .ROUTING(ROUTING_ALLOC) +  ) wst_in_vc_buf_i ( +    .clk           (clk),  +    .reset         (reset),  +    .s_axis_tdata  (s_axis_wst_tdata), +    .s_axis_tdest  (s_axis_wst_tdest), +    .s_axis_tlast  (s_axis_wst_tlast), +    .s_axis_tvalid (s_axis_wst_tvalid), +    .s_axis_tready (s_axis_wst_tready), +    .m_axis_tdata  (wst_i_tdata), +    .m_axis_tlast  (wst_i_tlast), +    .m_axis_tvalid (wst_i_tvalid), +    .m_axis_tready (wst_i_tready) +  ); + +  axis_ingress_vc_buff #( +    .WIDTH(WIDTH), .NUM_VCS(1), +    .SIZE(XB_BUFF_SIZE), +    .ROUTING(ROUTING_ALLOC) +  ) est_in_vc_buf_i ( +    .clk           (clk),  +    .reset         (reset),  +    .s_axis_tdata  (s_axis_est_tdata), +    .s_axis_tdest  (s_axis_est_tdest), +    .s_axis_tlast  (s_axis_est_tlast), +    .s_axis_tvalid (s_axis_est_tvalid), +    .s_axis_tready (s_axis_est_tready), +    .m_axis_tdata  (est_i_tdata), +    .m_axis_tlast  (est_i_tlast), +    .m_axis_tvalid (est_i_tvalid), +    .m_axis_tready (est_i_tready) +  ); + +  axis_ingress_vc_buff #( +    .WIDTH(WIDTH), .NUM_VCS(1), +    .SIZE(XB_BUFF_SIZE), +    .ROUTING(ROUTING_ALLOC) +  ) nor_in_vc_buf_i ( +    .clk           (clk),  +    .reset         (reset),  +    .s_axis_tdata  (s_axis_nor_tdata), +    .s_axis_tdest  (s_axis_nor_tdest), +    .s_axis_tlast  (s_axis_nor_tlast), +    .s_axis_tvalid (s_axis_nor_tvalid), +    .s_axis_tready (s_axis_nor_tready), +    .m_axis_tdata  (nor_i_tdata), +    .m_axis_tlast  (nor_i_tlast), +    .m_axis_tvalid (nor_i_tvalid), +    .m_axis_tready (nor_i_tready) +  ); + +  axis_ingress_vc_buff #( +    .WIDTH(WIDTH), .NUM_VCS(1), +    .SIZE(XB_BUFF_SIZE), +    .ROUTING(ROUTING_ALLOC) +  ) sou_in_vc_buf_i ( +    .clk           (clk),  +    .reset         (reset),  +    .s_axis_tdata  (s_axis_sou_tdata), +    .s_axis_tdest  (s_axis_sou_tdest), +    .s_axis_tlast  (s_axis_sou_tlast), +    .s_axis_tvalid (s_axis_sou_tvalid), +    .s_axis_tready (s_axis_sou_tready), +    .m_axis_tdata  (sou_i_tdata), +    .m_axis_tlast  (sou_i_tlast), +    .m_axis_tvalid (sou_i_tvalid), +    .m_axis_tready (sou_i_tready) +  ); + +  // ------------------------------------------------- +  // Input demuxes +  // ------------------------------------------------- + +  wire [WIDTH-1:0] t2t_tdata,  t2w_tdata,  t2e_tdata,  t2n_tdata,  t2s_tdata; +  wire             t2t_tlast,  t2w_tlast,  t2e_tlast,  t2n_tlast,  t2s_tlast; +  wire             t2t_tvalid, t2w_tvalid, t2e_tvalid, t2n_tvalid, t2s_tvalid; +  wire             t2t_tready, t2w_tready, t2e_tready, t2n_tready, t2s_tready; + +  wire [WIDTH-1:0] w2t_tdata,  w2e_tdata,  w2n_tdata,  w2s_tdata; +  wire             w2t_tlast,  w2e_tlast,  w2n_tlast,  w2s_tlast; +  wire             w2t_tvalid, w2e_tvalid, w2n_tvalid, w2s_tvalid; +  wire             w2t_tready, w2e_tready, w2n_tready, w2s_tready; + +  wire [WIDTH-1:0] e2t_tdata,  e2w_tdata,  e2n_tdata,  e2s_tdata; +  wire             e2t_tlast,  e2w_tlast,  e2n_tlast,  e2s_tlast; +  wire             e2t_tvalid, e2w_tvalid, e2n_tvalid, e2s_tvalid; +  wire             e2t_tready, e2w_tready, e2n_tready, e2s_tready; + +  wire [WIDTH-1:0] n2t_tdata,  n2s_tdata; +  wire             n2t_tlast,  n2s_tlast; +  wire             n2t_tvalid, n2s_tvalid; +  wire             n2t_tready, n2s_tready; + +  wire [WIDTH-1:0] s2t_tdata,  s2n_tdata; +  wire             s2t_tlast,  s2n_tlast; +  wire             s2t_tvalid, s2n_tvalid; +  wire             s2t_tready, s2n_tready; + +  wire [WIDTH-1:0] ter_i_hdr, wst_i_hdr, est_i_hdr, nor_i_hdr, sou_i_hdr; + +  axi_demux #( +    .WIDTH(WIDTH), .SIZE(5), +    .PRE_FIFO_SIZE(0 /* must be 0 */), .POST_FIFO_SIZE(0) +  ) ter_i_demux_i ( +    .clk      (clk), +    .reset    (reset), +    .clear    (1'b0), +    .header   (ter_i_hdr), +    .dest     (term_route(ter_i_hdr)), +    .i_tdata  (ter_i_tdata), +    .i_tlast  (ter_i_tlast), +    .i_tvalid (ter_i_tvalid), +    .i_tready (ter_i_tready), +    .o_tdata  ({t2s_tdata,  t2n_tdata,  t2e_tdata,  t2w_tdata,  t2t_tdata}), +    .o_tlast  ({t2s_tlast,  t2n_tlast,  t2e_tlast,  t2w_tlast,  t2t_tlast}), +    .o_tvalid ({t2s_tvalid, t2n_tvalid, t2e_tvalid, t2w_tvalid, t2t_tvalid}), +    .o_tready ({t2s_tready, t2n_tready, t2e_tready, t2w_tready, t2t_tready}) +  ); + +  axi_demux #( +    .WIDTH(WIDTH), .SIZE(4), +    .PRE_FIFO_SIZE(0 /* must be 0 */), .POST_FIFO_SIZE(0) +  ) wst_i_demux_i ( +    .clk      (clk), +    .reset    (reset), +    .clear    (1'b0), +    .header   (wst_i_hdr), +    .dest     (xdim_route(wst_i_hdr)), +    .i_tdata  (wst_i_tdata), +    .i_tlast  (wst_i_tlast), +    .i_tvalid (wst_i_tvalid), +    .i_tready (wst_i_tready), +    .o_tdata  ({w2s_tdata,  w2n_tdata,  w2e_tdata,  w2t_tdata}), +    .o_tlast  ({w2s_tlast,  w2n_tlast,  w2e_tlast,  w2t_tlast}), +    .o_tvalid ({w2s_tvalid, w2n_tvalid, w2e_tvalid, w2t_tvalid}), +    .o_tready ({w2s_tready, w2n_tready, w2e_tready, w2t_tready}) +  ); + +  axi_demux #( +    .WIDTH(WIDTH), .SIZE(4), +    .PRE_FIFO_SIZE(0 /* must be 0 */), .POST_FIFO_SIZE(0) +  ) est_i_demux_i ( +    .clk      (clk), +    .reset    (reset), +    .clear    (1'b0), +    .header   (est_i_hdr), +    .dest     (xdim_route(est_i_hdr)), +    .i_tdata  (est_i_tdata), +    .i_tlast  (est_i_tlast), +    .i_tvalid (est_i_tvalid), +    .i_tready (est_i_tready), +    .o_tdata  ({e2s_tdata,  e2n_tdata,  e2w_tdata,  e2t_tdata}), +    .o_tlast  ({e2s_tlast,  e2n_tlast,  e2w_tlast,  e2t_tlast}), +    .o_tvalid ({e2s_tvalid, e2n_tvalid, e2w_tvalid, e2t_tvalid}), +    .o_tready ({e2s_tready, e2n_tready, e2w_tready, e2t_tready}) +  ); + +  axi_demux #( +    .WIDTH(WIDTH), .SIZE(2), +    .PRE_FIFO_SIZE(0 /* must be 0 */), .POST_FIFO_SIZE(0) +  ) nor_i_demux_i ( +    .clk      (clk), +    .reset    (reset), +    .clear    (1'b0), +    .header   (nor_i_hdr), +    .dest     (ydim_route(nor_i_hdr)), +    .i_tdata  (nor_i_tdata), +    .i_tlast  (nor_i_tlast), +    .i_tvalid (nor_i_tvalid), +    .i_tready (nor_i_tready), +    .o_tdata  ({n2t_tdata,  n2s_tdata}), +    .o_tlast  ({n2t_tlast,  n2s_tlast}), +    .o_tvalid ({n2t_tvalid, n2s_tvalid}), +    .o_tready ({n2t_tready, n2s_tready}) +  ); + +  axi_demux #( +    .WIDTH(WIDTH), .SIZE(2), +    .PRE_FIFO_SIZE(0 /* must be 0 */), .POST_FIFO_SIZE(0) +  ) sou_i_demux_i ( +    .clk      (clk), +    .reset    (reset), +    .clear    (1'b0), +    .header   (sou_i_hdr), +    .dest     (ydim_route(sou_i_hdr)), +    .i_tdata  (sou_i_tdata), +    .i_tlast  (sou_i_tlast), +    .i_tvalid (sou_i_tvalid), +    .i_tready (sou_i_tready), +    .o_tdata  ({s2t_tdata,  s2n_tdata}), +    .o_tlast  ({s2t_tlast,  s2n_tlast}), +    .o_tvalid ({s2t_tvalid, s2n_tvalid}), +    .o_tready ({s2t_tready, s2n_tready}) +  ); + +  // ------------------------------------------------- +  // Output muxes +  // ------------------------------------------------- + +  axi_mux #( +    .WIDTH(WIDTH), .SIZE(5), +    .PRE_FIFO_SIZE(0), .POST_FIFO_SIZE(1) +  ) ter_o_mux_i ( +    .clk      (clk), +    .reset    (reset), +    .clear    (1'b0), +    .i_tdata  ({t2t_tdata,  w2t_tdata,  e2t_tdata,  n2t_tdata,  s2t_tdata}), +    .i_tlast  ({t2t_tlast,  w2t_tlast,  e2t_tlast,  n2t_tlast,  s2t_tlast}), +    .i_tvalid ({t2t_tvalid, w2t_tvalid, e2t_tvalid, n2t_tvalid, s2t_tvalid}), +    .i_tready ({t2t_tready, w2t_tready, e2t_tready, n2t_tready, s2t_tready}), +    .o_tdata  (m_axis_ter_tdata), +    .o_tlast  (m_axis_ter_tlast), +    .o_tvalid (m_axis_ter_tvalid), +    .o_tready (m_axis_ter_tready) +  ); + +  axi_mux #( +    .WIDTH(WIDTH), .SIZE(2), +    .PRE_FIFO_SIZE(0), .POST_FIFO_SIZE(1) +  ) wst_o_mux_i ( +    .clk      (clk), +    .reset    (reset), +    .clear    (1'b0), +    .i_tdata  ({t2w_tdata,  e2w_tdata}), +    .i_tlast  ({t2w_tlast,  e2w_tlast}), +    .i_tvalid ({t2w_tvalid, e2w_tvalid}), +    .i_tready ({t2w_tready, e2w_tready}), +    .o_tdata  (m_axis_wst_tdata), +    .o_tlast  (m_axis_wst_tlast), +    .o_tvalid (m_axis_wst_tvalid), +    .o_tready (m_axis_wst_tready) +  ); +  assign m_axis_wst_tdest = 1'b0; + +  axi_mux #( +    .WIDTH(WIDTH), .SIZE(2), +    .PRE_FIFO_SIZE(0), .POST_FIFO_SIZE(1) +  ) est_o_mux_i ( +    .clk      (clk), +    .reset    (reset), +    .clear    (1'b0), +    .i_tdata  ({t2e_tdata,  w2e_tdata}), +    .i_tlast  ({t2e_tlast,  w2e_tlast}), +    .i_tvalid ({t2e_tvalid, w2e_tvalid}), +    .i_tready ({t2e_tready, w2e_tready}), +    .o_tdata  (m_axis_est_tdata), +    .o_tlast  (m_axis_est_tlast), +    .o_tvalid (m_axis_est_tvalid), +     +     +    .o_tready (m_axis_est_tready) +  ); +  assign m_axis_est_tdest = 1'b0; + +  axi_mux #( +    .WIDTH(WIDTH), .SIZE(4), +    .PRE_FIFO_SIZE(0), .POST_FIFO_SIZE(1) +  ) nor_o_mux_i ( +    .clk      (clk), +    .reset    (reset), +    .clear    (1'b0), +    .i_tdata  ({t2n_tdata,  w2n_tdata,  e2n_tdata,  s2n_tdata}), +    .i_tlast  ({t2n_tlast,  w2n_tlast,  e2n_tlast,  s2n_tlast}), +    .i_tvalid ({t2n_tvalid, w2n_tvalid, e2n_tvalid, s2n_tvalid}), +    .i_tready ({t2n_tready, w2n_tready, e2n_tready, s2n_tready}), +    .o_tdata  (m_axis_nor_tdata), +    .o_tlast  (m_axis_nor_tlast), +    .o_tvalid (m_axis_nor_tvalid), +    .o_tready (m_axis_nor_tready) +  ); +  assign m_axis_nor_tdest = 1'b0; + +  axi_mux #( +    .WIDTH(WIDTH), .SIZE(4), +    .PRE_FIFO_SIZE(0), .POST_FIFO_SIZE(1) +  ) sou_o_mux_i ( +    .clk      (clk), +    .reset    (reset), +    .clear    (1'b0), +    .i_tdata  ({t2s_tdata,  w2s_tdata,  e2s_tdata,  n2s_tdata}), +    .i_tlast  ({t2s_tlast,  w2s_tlast,  e2s_tlast,  n2s_tlast}), +    .i_tvalid ({t2s_tvalid, w2s_tvalid, e2s_tvalid, n2s_tvalid}), +    .i_tready ({t2s_tready, w2s_tready, e2s_tready, n2s_tready}), +    .o_tdata  (m_axis_sou_tdata), +    .o_tlast  (m_axis_sou_tlast), +    .o_tvalid (m_axis_sou_tvalid), +    .o_tready (m_axis_sou_tready) +  ); +  assign m_axis_sou_tdest = 1'b0; + +endmodule + diff --git a/fpga/usrp3/lib/rfnoc/crossbar/mesh_2d_dor_router_single_sw.v b/fpga/usrp3/lib/rfnoc/crossbar/mesh_2d_dor_router_single_sw.v new file mode 100644 index 000000000..65cded545 --- /dev/null +++ b/fpga/usrp3/lib/rfnoc/crossbar/mesh_2d_dor_router_single_sw.v @@ -0,0 +1,398 @@ +// +// Copyright 2018 Ettus Research, A National Instruments Company +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// +// Module: mesh_2d_dor_router_single_sw +// Description:  +//   This module implements the router for a 2-dimentional (2d)  +//   mesh network that uses dimension order routing (dor) and has a  +//   single underlying switch (single_sw). It uses AXI-Stream for all of its +//   links.  +//   The mesh topology, routing algorithms and the router architecture is  +//   described in README.md in this directory.  +// Parameters: +//   - WIDTH: Width of the AXI-Stream data bus +//   - DIM_SIZE: Number of routers alone one dimension +//   - XB_ADDR_X: The X-coordinate of this router in the topology +//   - XB_ADDR_Y: The Y-coordinate of this router in the topology +//   - TERM_BUFF_SIZE: log2 of the ingress terminal buffer size (in words) +//   - XB_BUFF_SIZE: log2 of the ingress inter-router buffer size (in words) +//   - ROUTING_ALLOC: Algorithm to allocate routing paths between routers. +//     * WORMHOLE: Allocate route as soon as first word in pkt arrives +//     * CUT-THROUGH: Allocate route only after the full pkt arrives +//   - SWITCH_ALLOC: Algorithm to allocate the switch +//     * PRIO: Priority based. Priority: Y-dim > X-dim > Term +//     * ROUND-ROBIN: Round robin input port allocation +// Signals: +//   - *_axis_ter_*: Terminal ports (master/slave) +//   - *_axis_wst_*: Inter-router X-dim west connections (master/slave) +//   - *_axis_est_*: Inter-router X-dim east connections (master/slave) +//   - *_axis_nor_*: Inter-router X-dim north connections (master/slave) +//   - *_axis_sou_*: Inter-router X-dim south connections (master/slave) +// + +module mesh_2d_dor_router_single_sw #( +  parameter                        WIDTH          = 64, +  parameter                        DIM_SIZE       = 4, +  parameter [$clog2(DIM_SIZE)-1:0] XB_ADDR_X      = 0, +  parameter [$clog2(DIM_SIZE)-1:0] XB_ADDR_Y      = 0, +  parameter                        TERM_BUFF_SIZE = 5, +  parameter                        XB_BUFF_SIZE   = 5, +  parameter                        ROUTING_ALLOC  = "WORMHOLE",  // Routing (switching) method {WORMHOLE, CUT-THROUGH} +  parameter                        SWITCH_ALLOC   = "PRIO"       // Switch allocation algorithm {ROUND-ROBIN, PRIO} +) ( +  // Clocks and resets +  input  wire             clk, +  input  wire             reset, + +  // Terminal connections +  input  wire [WIDTH-1:0] s_axis_ter_tdata, +  input  wire             s_axis_ter_tlast, +  input  wire             s_axis_ter_tvalid, +  output wire             s_axis_ter_tready, +  output wire [WIDTH-1:0] m_axis_ter_tdata, +  output wire             m_axis_ter_tlast, +  output wire             m_axis_ter_tvalid, +  input  wire             m_axis_ter_tready, + +  // West inter-router connections +  input  wire [WIDTH-1:0] s_axis_wst_tdata, +  input  wire [0:0]       s_axis_wst_tdest, +  input  wire             s_axis_wst_tlast, +  input  wire             s_axis_wst_tvalid, +  output wire             s_axis_wst_tready, +  output wire [WIDTH-1:0] m_axis_wst_tdata, +  output wire [0:0]       m_axis_wst_tdest, +  output wire             m_axis_wst_tlast, +  output wire             m_axis_wst_tvalid, +  input  wire             m_axis_wst_tready, + +  // East inter-router connections +  input  wire [WIDTH-1:0] s_axis_est_tdata, +  input  wire [0:0]       s_axis_est_tdest, +  input  wire             s_axis_est_tlast, +  input  wire             s_axis_est_tvalid, +  output wire             s_axis_est_tready, +  output wire [WIDTH-1:0] m_axis_est_tdata, +  output wire [0:0]       m_axis_est_tdest, +  output wire             m_axis_est_tlast, +  output wire             m_axis_est_tvalid, +  input  wire             m_axis_est_tready, + +  // North inter-router connections +  input  wire [WIDTH-1:0] s_axis_nor_tdata, +  input  wire [0:0]       s_axis_nor_tdest, +  input  wire             s_axis_nor_tlast, +  input  wire             s_axis_nor_tvalid, +  output wire             s_axis_nor_tready, +  output wire [WIDTH-1:0] m_axis_nor_tdata, +  output wire [0:0]       m_axis_nor_tdest, +  output wire             m_axis_nor_tlast, +  output wire             m_axis_nor_tvalid, +  input  wire             m_axis_nor_tready, + +  // South inter-router connections +  input  wire [WIDTH-1:0] s_axis_sou_tdata, +  input  wire [0:0]       s_axis_sou_tdest, +  input  wire             s_axis_sou_tlast, +  input  wire             s_axis_sou_tvalid, +  output wire             s_axis_sou_tready, +  output wire [WIDTH-1:0] m_axis_sou_tdata, +  output wire [0:0]       m_axis_sou_tdest, +  output wire             m_axis_sou_tlast, +  output wire             m_axis_sou_tvalid, +  input  wire             m_axis_sou_tready +); +  // ------------------------------------------------- +  // Routing functions +  // ------------------------------------------------- + +  // mesh_node_mapping.vh file contains the mapping between the node number +  // and its XY coordinates. It is autogenerated and defines the node_to_xdst +  // and node_to_ydst functions. +  `include "mesh_node_mapping.vh" + +  localparam [2:0] SW_DEST_TER  = 3'd0; +  localparam [2:0] SW_DEST_WST  = 3'd1; +  localparam [2:0] SW_DEST_EST  = 3'd2; +  localparam [2:0] SW_DEST_NOR  = 3'd3; +  localparam [2:0] SW_DEST_SOU  = 3'd4; +  localparam [2:0] SW_NUM_DESTS = 3'd5; + +  // The compute_switch_tdest function is the destination selector +  // i.e. it will inspecte the bottom $clog2(DIM_SIZE)*2 bits of the +  // first word of a packet and determine the destination of the packet.  +  function [3:0] compute_switch_tdest; +    input [WIDTH-1:0] header; +    input [3:0]       src; +    reg [$clog2(DIM_SIZE)-1:0] xdst, ydst; +    reg signed [$clog2(DIM_SIZE):0] xdiff, ydiff; +  begin +    xdst  = node_to_xdst(header); +    ydst  = node_to_ydst(header); +    xdiff = xdst - XB_ADDR_X; +    ydiff = ydst - XB_ADDR_Y; +    // Routing logic +    // - MSB is the VC, 3 LSBs are the router destination +    // - VC in a mesh is always 0 +    if (xdiff == 'd0 && ydiff == 'd0) begin +      // VC=0 because terminals don't have VCs +      compute_switch_tdest = {1'b0, SW_DEST_TER}; +    end else if (xdiff == 'd0) begin +      // VC=1 for CCW turns and VC=0 for everything else +      if (ydiff < 0) +        compute_switch_tdest = {(src == SW_DEST_WST), SW_DEST_NOR}; +      else +        compute_switch_tdest = {(src == SW_DEST_EST), SW_DEST_SOU}; +    end else begin +      // VC=0 because east-west paths don't have VCs +      if (xdiff < 0) +        compute_switch_tdest = {1'b0, SW_DEST_WST};  +      else +        compute_switch_tdest = {1'b0, SW_DEST_EST}; +    end +    if (xdst != 'hx && ydst != 'hx) begin +      if (XB_ADDR_X == 0 && compute_switch_tdest == SW_DEST_WST) +        $display("Illegal route chosen: WEST. xdst=%d, ydst=%d, xaddr=%d, yaddr=%d", xdst, ydst, XB_ADDR_X, XB_ADDR_Y); +      if (XB_ADDR_X == DIM_SIZE-1 && compute_switch_tdest == SW_DEST_EST) +        $display("Illegal route chosen: EAST. xdst=%d, ydst=%d, xaddr=%d, yaddr=%d", xdst, ydst, XB_ADDR_X, XB_ADDR_Y); +      if (XB_ADDR_Y == 0 && compute_switch_tdest == SW_DEST_NOR) +        $display("Illegal route chosen: NORTH. xdst=%d, ydst=%d, xaddr=%d, yaddr=%d", xdst, ydst, XB_ADDR_X, XB_ADDR_Y); +      if (XB_ADDR_Y == DIM_SIZE-1 && compute_switch_tdest == SW_DEST_SOU) +        $display("Illegal route chosen: SOUTH. xdst=%d, ydst=%d, xaddr=%d, yaddr=%d", xdst, ydst, XB_ADDR_X, XB_ADDR_Y); +    end +    //$display("xdst=%d, ydst=%d, xaddr=%d, yaddr=%d, dst=%d", xdst, ydst, XB_ADDR_X, XB_ADDR_Y, compute_switch_tdest); +  end +  endfunction + +  // The compute_switch_alloc function is the switch allocation function  +  // i.e. it chooses which input port reserves the switch for packet transfer. +  // After the switch is allocated, all other ports will be backpressured until +  // the packet finishes transferring. +  function [2:0] compute_switch_alloc; +    input [4:0] pkt_waiting; +    input [2:0] last_alloc; +  begin +    if (pkt_waiting == 5'b00000) begin +      compute_switch_alloc = SW_DEST_TER; +    end else if (pkt_waiting == 5'b00001) begin +      compute_switch_alloc = SW_DEST_TER; +    end else if (pkt_waiting == 5'b00010) begin +      compute_switch_alloc = SW_DEST_WST; +    end else if (pkt_waiting == 5'b00100) begin +      compute_switch_alloc = SW_DEST_EST; +    end else if (pkt_waiting == 5'b01000) begin +      compute_switch_alloc = SW_DEST_NOR; +    end else if (pkt_waiting == 5'b10000) begin +      compute_switch_alloc = SW_DEST_SOU; +    end else begin +      if (SWITCH_ALLOC == "PRIO") begin +        // Priority: South > East > North > West > Term +        if (pkt_waiting[SW_DEST_SOU]) +          compute_switch_alloc = SW_DEST_SOU; +        else if (pkt_waiting[SW_DEST_EST]) +          compute_switch_alloc = SW_DEST_EST; +        else if (pkt_waiting[SW_DEST_NOR]) +          compute_switch_alloc = SW_DEST_NOR; +        else if (pkt_waiting[SW_DEST_WST]) +          compute_switch_alloc = SW_DEST_WST; +        else +          compute_switch_alloc = SW_DEST_TER; +      end else begin +        // Round-robin +        if (pkt_waiting[(last_alloc + 3'd1) % SW_NUM_DESTS]) +          compute_switch_alloc = (last_alloc + 3'd1) % SW_NUM_DESTS; +        else if (pkt_waiting[(last_alloc + 3'd2) % SW_NUM_DESTS]) +          compute_switch_alloc = (last_alloc + 3'd2) % SW_NUM_DESTS; +        else if (pkt_waiting[(last_alloc + 3'd3) % SW_NUM_DESTS]) +          compute_switch_alloc = (last_alloc + 3'd3) % SW_NUM_DESTS; +        else if (pkt_waiting[(last_alloc + 3'd4) % SW_NUM_DESTS]) +          compute_switch_alloc = (last_alloc + 3'd4) % SW_NUM_DESTS; +        else +          compute_switch_alloc = last_alloc; +      end +    end +    //$display("pkt_waiting=%b, alloc=%d, last_alloc=%d", pkt_waiting, compute_switch_alloc, last_alloc); +  end +  endfunction + +  // ------------------------------------------------- +  // Input buffers +  // ------------------------------------------------- +  wire [WIDTH-1:0] ter_i_tdata; +  wire [3:0]       ter_i_tdest; +  wire             ter_i_tlast; +  wire             ter_i_tvalid; +  wire             ter_i_tready; + +  // Data coming in from the terminal is gated until a full packet arrives +  // in order to minimize the switch allocation time per packet. +  axi_packet_gate #( +    .WIDTH(WIDTH), .SIZE(TERM_BUFF_SIZE) +  ) term_in_pkt_gate_i ( +    .clk      (clk),  +    .reset    (reset),  +    .clear    (1'b0), +    .i_tdata  (s_axis_ter_tdata), +    .i_tlast  (s_axis_ter_tlast), +    .i_tvalid (s_axis_ter_tvalid), +    .i_tready (s_axis_ter_tready), +    .i_terror (1'b0), +    .o_tdata  (ter_i_tdata), +    .o_tlast  (ter_i_tlast), +    .o_tvalid (ter_i_tvalid), +    .o_tready (ter_i_tready) +  ); +  assign ter_i_tdest = compute_switch_tdest(ter_i_tdata, SW_DEST_TER); + +  wire [WIDTH-1:0] wst_i_tdata,  est_i_tdata,  nor_i_tdata,  sou_i_tdata; +  wire [3:0]       wst_i_tdest,  est_i_tdest,  nor_i_tdest,  sou_i_tdest; +  wire             wst_i_tlast,  est_i_tlast,  nor_i_tlast,  sou_i_tlast; +  wire             wst_i_tvalid, est_i_tvalid, nor_i_tvalid, sou_i_tvalid; +  wire             wst_i_tready, est_i_tready, nor_i_tready, sou_i_tready; + +  axis_ingress_vc_buff #( +    .WIDTH(WIDTH), .NUM_VCS(1), +    .SIZE(XB_BUFF_SIZE), +    .ROUTING(ROUTING_ALLOC) +  ) wst_in_vc_buf_i ( +    .clk           (clk),  +    .reset         (reset),  +    .s_axis_tdata  (s_axis_wst_tdata), +    .s_axis_tdest  (s_axis_wst_tdest), +    .s_axis_tlast  (s_axis_wst_tlast), +    .s_axis_tvalid (s_axis_wst_tvalid), +    .s_axis_tready (s_axis_wst_tready), +    .m_axis_tdata  (wst_i_tdata), +    .m_axis_tlast  (wst_i_tlast), +    .m_axis_tvalid (wst_i_tvalid), +    .m_axis_tready (wst_i_tready) +  ); +  assign wst_i_tdest = compute_switch_tdest(wst_i_tdata, SW_DEST_WST); + +  axis_ingress_vc_buff #( +    .WIDTH(WIDTH), .NUM_VCS(1), +    .SIZE(XB_BUFF_SIZE), +    .ROUTING(ROUTING_ALLOC) +  ) est_in_vc_buf_i ( +    .clk           (clk),  +    .reset         (reset),  +    .s_axis_tdata  (s_axis_est_tdata), +    .s_axis_tdest  (s_axis_est_tdest), +    .s_axis_tlast  (s_axis_est_tlast), +    .s_axis_tvalid (s_axis_est_tvalid), +    .s_axis_tready (s_axis_est_tready), +    .m_axis_tdata  (est_i_tdata), +    .m_axis_tlast  (est_i_tlast), +    .m_axis_tvalid (est_i_tvalid), +    .m_axis_tready (est_i_tready) +  ); +  assign est_i_tdest = compute_switch_tdest(est_i_tdata, SW_DEST_EST); + +  axis_ingress_vc_buff #( +    .WIDTH(WIDTH), .NUM_VCS(2), // Only north-south traffic has VCs +    .SIZE(XB_BUFF_SIZE), +    .ROUTING(ROUTING_ALLOC) +  ) nor_in_vc_buf_i ( +    .clk           (clk),  +    .reset         (reset),  +    .s_axis_tdata  (s_axis_nor_tdata), +    .s_axis_tdest  (s_axis_nor_tdest), +    .s_axis_tlast  (s_axis_nor_tlast), +    .s_axis_tvalid (s_axis_nor_tvalid), +    .s_axis_tready (s_axis_nor_tready), +    .m_axis_tdata  (nor_i_tdata), +    .m_axis_tlast  (nor_i_tlast), +    .m_axis_tvalid (nor_i_tvalid), +    .m_axis_tready (nor_i_tready) +  ); +  assign nor_i_tdest = compute_switch_tdest(nor_i_tdata, SW_DEST_NOR); + +  axis_ingress_vc_buff #( +    .WIDTH(WIDTH), .NUM_VCS(2), // Only north-south traffic has VCs +    .SIZE(XB_BUFF_SIZE), +    .ROUTING(ROUTING_ALLOC) +  ) sou_in_vc_buf_i ( +    .clk           (clk),  +    .reset         (reset),  +    .s_axis_tdata  (s_axis_sou_tdata), +    .s_axis_tdest  (s_axis_sou_tdest), +    .s_axis_tlast  (s_axis_sou_tlast), +    .s_axis_tvalid (s_axis_sou_tvalid), +    .s_axis_tready (s_axis_sou_tready), +    .m_axis_tdata  (sou_i_tdata), +    .m_axis_tlast  (sou_i_tlast), +    .m_axis_tvalid (sou_i_tvalid), +    .m_axis_tready (sou_i_tready) +  ); +  assign sou_i_tdest = compute_switch_tdest(sou_i_tdata, SW_DEST_SOU); + +  //------------------------------------------------- +  // Switch +  //------------------------------------------------- +  // Track the input packet state +  localparam [0:0] PKT_ST_HEAD = 1'b0; +  localparam [0:0] PKT_ST_BODY = 1'b1; +  reg [0:0] pkt_state = PKT_ST_HEAD; + +  // The switch only accept packets on a single port at a time. +  wire sw_in_ready = |({sou_i_tready, nor_i_tready, est_i_tready, wst_i_tready, ter_i_tready}); +  wire sw_in_valid = |({sou_i_tvalid, nor_i_tvalid, est_i_tvalid, wst_i_tvalid, ter_i_tvalid}); +  wire sw_in_last  = |({sou_i_tlast & sou_i_tvalid, nor_i_tlast & nor_i_tvalid, +                        est_i_tlast & est_i_tvalid, wst_i_tlast & wst_i_tvalid, +                        ter_i_tlast & ter_i_tvalid}); + +  always @(posedge clk) begin +    if (reset) begin +      pkt_state <= PKT_ST_HEAD; +    end else if (sw_in_valid & sw_in_ready) begin +      pkt_state <= sw_in_last ? PKT_ST_HEAD : PKT_ST_BODY; +    end +  end + +  // The switch requires the allocation to stay valid until the +  // end of the packet. We also might need to keep the previous +  // packet's allocation to compute the current one +  wire [2:0] switch_alloc; +  reg  [2:0] prev_switch_alloc = SW_DEST_TER; +  reg  [2:0] pkt_switch_alloc  = SW_DEST_TER; + +  always @(posedge clk) begin +    if (reset) begin +      prev_switch_alloc <= SW_DEST_TER; +      pkt_switch_alloc <= SW_DEST_TER; +    end else if (sw_in_valid & sw_in_ready) begin +      if (pkt_state == PKT_ST_HEAD) +        pkt_switch_alloc <= switch_alloc; +      if (sw_in_last) +        prev_switch_alloc <= switch_alloc; +    end +  end + +  assign switch_alloc = (sw_in_valid && pkt_state == PKT_ST_HEAD) ?  +    compute_switch_alloc({sou_i_tvalid, nor_i_tvalid, est_i_tvalid, wst_i_tvalid, ter_i_tvalid}, prev_switch_alloc) : +    pkt_switch_alloc; + +  wire ter_tdest_discard; +  axis_switch #( +    .DATA_W(WIDTH), .DEST_W(1), .IN_PORTS(5), .OUT_PORTS(5) +  ) switch_i ( +    .clk           (clk), +    .reset         (reset), +    .s_axis_tdata  ({sou_i_tdata , nor_i_tdata , est_i_tdata , wst_i_tdata , ter_i_tdata }), +    .s_axis_tdest  ({sou_i_tdest , nor_i_tdest , est_i_tdest , wst_i_tdest , ter_i_tdest }),  +    .s_axis_tlast  ({sou_i_tlast , nor_i_tlast , est_i_tlast , wst_i_tlast , ter_i_tlast }), +    .s_axis_tvalid ({sou_i_tvalid, nor_i_tvalid, est_i_tvalid, wst_i_tvalid, ter_i_tvalid}), +    .s_axis_tready ({sou_i_tready, nor_i_tready, est_i_tready, wst_i_tready, ter_i_tready}), +    .s_axis_alloc  (switch_alloc), +    .m_axis_tdata  ({m_axis_sou_tdata,  m_axis_nor_tdata,  m_axis_est_tdata,  m_axis_wst_tdata,  m_axis_ter_tdata }), +    .m_axis_tdest  ({m_axis_sou_tdest,  m_axis_nor_tdest,  m_axis_est_tdest,  m_axis_wst_tdest,  ter_tdest_discard}), +    .m_axis_tlast  ({m_axis_sou_tlast,  m_axis_nor_tlast,  m_axis_est_tlast,  m_axis_wst_tlast,  m_axis_ter_tlast }), +    .m_axis_tvalid ({m_axis_sou_tvalid, m_axis_nor_tvalid, m_axis_est_tvalid, m_axis_wst_tvalid, m_axis_ter_tvalid}), +    .m_axis_tready ({m_axis_sou_tready, m_axis_nor_tready, m_axis_est_tready, m_axis_wst_tready, m_axis_ter_tready}) +  ); + + +endmodule + diff --git a/fpga/usrp3/lib/rfnoc/crossbar/mesh_node_mapping.vh b/fpga/usrp3/lib/rfnoc/crossbar/mesh_node_mapping.vh new file mode 100644 index 000000000..466b0c615 --- /dev/null +++ b/fpga/usrp3/lib/rfnoc/crossbar/mesh_node_mapping.vh @@ -0,0 +1,294 @@ +// Copyright 2018 Ettus Research, A National Instruments Company +// SPDX-License-Identifier: LGPL-3.0-or-later +// +// Autogenerated file. Do not modify. +// $ ./gen_node_to_coord_mapping.py --dimsize 16 --pattern spiral + +parameter CLOG2_DIM_SIZE = $clog2(DIM_SIZE); //Vivado workaround + + +// DIM_SIZE = 2, PATTERN = SPIRAL +//------------------------------------ +//     0    1 +//     3    2 +localparam [3:0] XCOORD_DIM_002 = {1'd0,1'd1,1'd1,1'd0}; +localparam [3:0] YCOORD_DIM_002 = {1'd1,1'd1,1'd0,1'd0}; + +// DIM_SIZE = 3, PATTERN = SPIRAL +//------------------------------------ +//     6    7    8 +//     5    0    1 +//     4    3    2 +localparam [17:0] XCOORD_DIM_003 = {2'd2,2'd1,2'd0,2'd0,2'd0,2'd1,2'd2,2'd2,2'd1}; +localparam [17:0] YCOORD_DIM_003 = {2'd0,2'd0,2'd0,2'd1,2'd2,2'd2,2'd2,2'd1,2'd1}; + +// DIM_SIZE = 4, PATTERN = SPIRAL +//------------------------------------ +//     6    7    8    9 +//     5    0    1   10 +//     4    3    2   11 +//    15   14   13   12 +localparam [31:0] XCOORD_DIM_004 = {2'd0,2'd1,2'd2,2'd3,2'd3,2'd3,2'd3,2'd2,2'd1,2'd0,2'd0,2'd0,2'd1,2'd2,2'd2,2'd1}; +localparam [31:0] YCOORD_DIM_004 = {2'd3,2'd3,2'd3,2'd3,2'd2,2'd1,2'd0,2'd0,2'd0,2'd0,2'd1,2'd2,2'd2,2'd2,2'd1,2'd1}; + +// DIM_SIZE = 5, PATTERN = SPIRAL +//------------------------------------ +//    20   21   22   23   24 +//    19    6    7    8    9 +//    18    5    0    1   10 +//    17    4    3    2   11 +//    16   15   14   13   12 +localparam [74:0] XCOORD_DIM_005 = {3'd4,3'd3,3'd2,3'd1,3'd0,3'd0,3'd0,3'd0,3'd0,3'd1,3'd2,3'd3,3'd4,3'd4,3'd4,3'd4,3'd3,3'd2,3'd1,3'd1,3'd1,3'd2,3'd3,3'd3,3'd2}; +localparam [74:0] YCOORD_DIM_005 = {3'd0,3'd0,3'd0,3'd0,3'd0,3'd1,3'd2,3'd3,3'd4,3'd4,3'd4,3'd4,3'd4,3'd3,3'd2,3'd1,3'd1,3'd1,3'd1,3'd2,3'd3,3'd3,3'd3,3'd2,3'd2}; + +// DIM_SIZE = 6, PATTERN = SPIRAL +//------------------------------------ +//    20   21   22   23   24   25 +//    19    6    7    8    9   26 +//    18    5    0    1   10   27 +//    17    4    3    2   11   28 +//    16   15   14   13   12   29 +//    35   34   33   32   31   30 +localparam [107:0] XCOORD_DIM_006 = {3'd0,3'd1,3'd2,3'd3,3'd4,3'd5,3'd5,3'd5,3'd5,3'd5,3'd5,3'd4,3'd3,3'd2,3'd1,3'd0,3'd0,3'd0,3'd0,3'd0,3'd1,3'd2,3'd3,3'd4,3'd4,3'd4,3'd4,3'd3,3'd2,3'd1,3'd1,3'd1,3'd2,3'd3,3'd3,3'd2}; +localparam [107:0] YCOORD_DIM_006 = {3'd5,3'd5,3'd5,3'd5,3'd5,3'd5,3'd4,3'd3,3'd2,3'd1,3'd0,3'd0,3'd0,3'd0,3'd0,3'd0,3'd1,3'd2,3'd3,3'd4,3'd4,3'd4,3'd4,3'd4,3'd3,3'd2,3'd1,3'd1,3'd1,3'd1,3'd2,3'd3,3'd3,3'd3,3'd2,3'd2}; + +// DIM_SIZE = 7, PATTERN = SPIRAL +//------------------------------------ +//    42   43   44   45   46   47   48 +//    41   20   21   22   23   24   25 +//    40   19    6    7    8    9   26 +//    39   18    5    0    1   10   27 +//    38   17    4    3    2   11   28 +//    37   16   15   14   13   12   29 +//    36   35   34   33   32   31   30 +localparam [146:0] XCOORD_DIM_007 = {3'd6,3'd5,3'd4,3'd3,3'd2,3'd1,3'd0,3'd0,3'd0,3'd0,3'd0,3'd0,3'd0,3'd1,3'd2,3'd3,3'd4,3'd5,3'd6,3'd6,3'd6,3'd6,3'd6,3'd6,3'd5,3'd4,3'd3,3'd2,3'd1,3'd1,3'd1,3'd1,3'd1,3'd2,3'd3,3'd4,3'd5,3'd5,3'd5,3'd5,3'd4,3'd3,3'd2,3'd2,3'd2,3'd3,3'd4,3'd4,3'd3}; +localparam [146:0] YCOORD_DIM_007 = {3'd0,3'd0,3'd0,3'd0,3'd0,3'd0,3'd0,3'd1,3'd2,3'd3,3'd4,3'd5,3'd6,3'd6,3'd6,3'd6,3'd6,3'd6,3'd6,3'd5,3'd4,3'd3,3'd2,3'd1,3'd1,3'd1,3'd1,3'd1,3'd1,3'd2,3'd3,3'd4,3'd5,3'd5,3'd5,3'd5,3'd5,3'd4,3'd3,3'd2,3'd2,3'd2,3'd2,3'd3,3'd4,3'd4,3'd4,3'd3,3'd3}; + +// DIM_SIZE = 8, PATTERN = SPIRAL +//------------------------------------ +//    42   43   44   45   46   47   48   49 +//    41   20   21   22   23   24   25   50 +//    40   19    6    7    8    9   26   51 +//    39   18    5    0    1   10   27   52 +//    38   17    4    3    2   11   28   53 +//    37   16   15   14   13   12   29   54 +//    36   35   34   33   32   31   30   55 +//    63   62   61   60   59   58   57   56 +localparam [191:0] XCOORD_DIM_008 = {3'd0,3'd1,3'd2,3'd3,3'd4,3'd5,3'd6,3'd7,3'd7,3'd7,3'd7,3'd7,3'd7,3'd7,3'd7,3'd6,3'd5,3'd4,3'd3,3'd2,3'd1,3'd0,3'd0,3'd0,3'd0,3'd0,3'd0,3'd0,3'd1,3'd2,3'd3,3'd4,3'd5,3'd6,3'd6,3'd6,3'd6,3'd6,3'd6,3'd5,3'd4,3'd3,3'd2,3'd1,3'd1,3'd1,3'd1,3'd1,3'd2,3'd3,3'd4,3'd5,3'd5,3'd5,3'd5,3'd4,3'd3,3'd2,3'd2,3'd2,3'd3,3'd4,3'd4,3'd3}; +localparam [191:0] YCOORD_DIM_008 = {3'd7,3'd7,3'd7,3'd7,3'd7,3'd7,3'd7,3'd7,3'd6,3'd5,3'd4,3'd3,3'd2,3'd1,3'd0,3'd0,3'd0,3'd0,3'd0,3'd0,3'd0,3'd0,3'd1,3'd2,3'd3,3'd4,3'd5,3'd6,3'd6,3'd6,3'd6,3'd6,3'd6,3'd6,3'd5,3'd4,3'd3,3'd2,3'd1,3'd1,3'd1,3'd1,3'd1,3'd1,3'd2,3'd3,3'd4,3'd5,3'd5,3'd5,3'd5,3'd5,3'd4,3'd3,3'd2,3'd2,3'd2,3'd2,3'd3,3'd4,3'd4,3'd4,3'd3,3'd3}; + +// DIM_SIZE = 9, PATTERN = SPIRAL +//------------------------------------ +//    72   73   74   75   76   77   78   79   80 +//    71   42   43   44   45   46   47   48   49 +//    70   41   20   21   22   23   24   25   50 +//    69   40   19    6    7    8    9   26   51 +//    68   39   18    5    0    1   10   27   52 +//    67   38   17    4    3    2   11   28   53 +//    66   37   16   15   14   13   12   29   54 +//    65   36   35   34   33   32   31   30   55 +//    64   63   62   61   60   59   58   57   56 +localparam [323:0] XCOORD_DIM_009 = {4'd8,4'd7,4'd6,4'd5,4'd4,4'd3,4'd2,4'd1,4'd0,4'd0,4'd0,4'd0,4'd0,4'd0,4'd0,4'd0,4'd0,4'd1,4'd2,4'd3,4'd4,4'd5,4'd6,4'd7,4'd8,4'd8,4'd8,4'd8,4'd8,4'd8,4'd8,4'd8,4'd7,4'd6,4'd5,4'd4,4'd3,4'd2,4'd1,4'd1,4'd1,4'd1,4'd1,4'd1,4'd1,4'd2,4'd3,4'd4,4'd5,4'd6,4'd7,4'd7,4'd7,4'd7,4'd7,4'd7,4'd6,4'd5,4'd4,4'd3,4'd2,4'd2,4'd2,4'd2,4'd2,4'd3,4'd4,4'd5,4'd6,4'd6,4'd6,4'd6,4'd5,4'd4,4'd3,4'd3,4'd3,4'd4,4'd5,4'd5,4'd4}; +localparam [323:0] YCOORD_DIM_009 = {4'd0,4'd0,4'd0,4'd0,4'd0,4'd0,4'd0,4'd0,4'd0,4'd1,4'd2,4'd3,4'd4,4'd5,4'd6,4'd7,4'd8,4'd8,4'd8,4'd8,4'd8,4'd8,4'd8,4'd8,4'd8,4'd7,4'd6,4'd5,4'd4,4'd3,4'd2,4'd1,4'd1,4'd1,4'd1,4'd1,4'd1,4'd1,4'd1,4'd2,4'd3,4'd4,4'd5,4'd6,4'd7,4'd7,4'd7,4'd7,4'd7,4'd7,4'd7,4'd6,4'd5,4'd4,4'd3,4'd2,4'd2,4'd2,4'd2,4'd2,4'd2,4'd3,4'd4,4'd5,4'd6,4'd6,4'd6,4'd6,4'd6,4'd5,4'd4,4'd3,4'd3,4'd3,4'd3,4'd4,4'd5,4'd5,4'd5,4'd4,4'd4}; + +// DIM_SIZE = 10, PATTERN = SPIRAL +//------------------------------------ +//    72   73   74   75   76   77   78   79   80   81 +//    71   42   43   44   45   46   47   48   49   82 +//    70   41   20   21   22   23   24   25   50   83 +//    69   40   19    6    7    8    9   26   51   84 +//    68   39   18    5    0    1   10   27   52   85 +//    67   38   17    4    3    2   11   28   53   86 +//    66   37   16   15   14   13   12   29   54   87 +//    65   36   35   34   33   32   31   30   55   88 +//    64   63   62   61   60   59   58   57   56   89 +//    99   98   97   96   95   94   93   92   91   90 +localparam [399:0] XCOORD_DIM_010 = {4'd0,4'd1,4'd2,4'd3,4'd4,4'd5,4'd6,4'd7,4'd8,4'd9,4'd9,4'd9,4'd9,4'd9,4'd9,4'd9,4'd9,4'd9,4'd9,4'd8,4'd7,4'd6,4'd5,4'd4,4'd3,4'd2,4'd1,4'd0,4'd0,4'd0,4'd0,4'd0,4'd0,4'd0,4'd0,4'd0,4'd1,4'd2,4'd3,4'd4,4'd5,4'd6,4'd7,4'd8,4'd8,4'd8,4'd8,4'd8,4'd8,4'd8,4'd8,4'd7,4'd6,4'd5,4'd4,4'd3,4'd2,4'd1,4'd1,4'd1,4'd1,4'd1,4'd1,4'd1,4'd2,4'd3,4'd4,4'd5,4'd6,4'd7,4'd7,4'd7,4'd7,4'd7,4'd7,4'd6,4'd5,4'd4,4'd3,4'd2,4'd2,4'd2,4'd2,4'd2,4'd3,4'd4,4'd5,4'd6,4'd6,4'd6,4'd6,4'd5,4'd4,4'd3,4'd3,4'd3,4'd4,4'd5,4'd5,4'd4}; +localparam [399:0] YCOORD_DIM_010 = {4'd9,4'd9,4'd9,4'd9,4'd9,4'd9,4'd9,4'd9,4'd9,4'd9,4'd8,4'd7,4'd6,4'd5,4'd4,4'd3,4'd2,4'd1,4'd0,4'd0,4'd0,4'd0,4'd0,4'd0,4'd0,4'd0,4'd0,4'd0,4'd1,4'd2,4'd3,4'd4,4'd5,4'd6,4'd7,4'd8,4'd8,4'd8,4'd8,4'd8,4'd8,4'd8,4'd8,4'd8,4'd7,4'd6,4'd5,4'd4,4'd3,4'd2,4'd1,4'd1,4'd1,4'd1,4'd1,4'd1,4'd1,4'd1,4'd2,4'd3,4'd4,4'd5,4'd6,4'd7,4'd7,4'd7,4'd7,4'd7,4'd7,4'd7,4'd6,4'd5,4'd4,4'd3,4'd2,4'd2,4'd2,4'd2,4'd2,4'd2,4'd3,4'd4,4'd5,4'd6,4'd6,4'd6,4'd6,4'd6,4'd5,4'd4,4'd3,4'd3,4'd3,4'd3,4'd4,4'd5,4'd5,4'd5,4'd4,4'd4}; + +// DIM_SIZE = 11, PATTERN = SPIRAL +//------------------------------------ +//   110  111  112  113  114  115  116  117  118  119  120 +//   109   72   73   74   75   76   77   78   79   80   81 +//   108   71   42   43   44   45   46   47   48   49   82 +//   107   70   41   20   21   22   23   24   25   50   83 +//   106   69   40   19    6    7    8    9   26   51   84 +//   105   68   39   18    5    0    1   10   27   52   85 +//   104   67   38   17    4    3    2   11   28   53   86 +//   103   66   37   16   15   14   13   12   29   54   87 +//   102   65   36   35   34   33   32   31   30   55   88 +//   101   64   63   62   61   60   59   58   57   56   89 +//   100   99   98   97   96   95   94   93   92   91   90 +localparam [483:0] XCOORD_DIM_011 = {4'd10,4'd9,4'd8,4'd7,4'd6,4'd5,4'd4,4'd3,4'd2,4'd1,4'd0,4'd0,4'd0,4'd0,4'd0,4'd0,4'd0,4'd0,4'd0,4'd0,4'd0,4'd1,4'd2,4'd3,4'd4,4'd5,4'd6,4'd7,4'd8,4'd9,4'd10,4'd10,4'd10,4'd10,4'd10,4'd10,4'd10,4'd10,4'd10,4'd10,4'd9,4'd8,4'd7,4'd6,4'd5,4'd4,4'd3,4'd2,4'd1,4'd1,4'd1,4'd1,4'd1,4'd1,4'd1,4'd1,4'd1,4'd2,4'd3,4'd4,4'd5,4'd6,4'd7,4'd8,4'd9,4'd9,4'd9,4'd9,4'd9,4'd9,4'd9,4'd9,4'd8,4'd7,4'd6,4'd5,4'd4,4'd3,4'd2,4'd2,4'd2,4'd2,4'd2,4'd2,4'd2,4'd3,4'd4,4'd5,4'd6,4'd7,4'd8,4'd8,4'd8,4'd8,4'd8,4'd8,4'd7,4'd6,4'd5,4'd4,4'd3,4'd3,4'd3,4'd3,4'd3,4'd4,4'd5,4'd6,4'd7,4'd7,4'd7,4'd7,4'd6,4'd5,4'd4,4'd4,4'd4,4'd5,4'd6,4'd6,4'd5}; +localparam [483:0] YCOORD_DIM_011 = {4'd0,4'd0,4'd0,4'd0,4'd0,4'd0,4'd0,4'd0,4'd0,4'd0,4'd0,4'd1,4'd2,4'd3,4'd4,4'd5,4'd6,4'd7,4'd8,4'd9,4'd10,4'd10,4'd10,4'd10,4'd10,4'd10,4'd10,4'd10,4'd10,4'd10,4'd10,4'd9,4'd8,4'd7,4'd6,4'd5,4'd4,4'd3,4'd2,4'd1,4'd1,4'd1,4'd1,4'd1,4'd1,4'd1,4'd1,4'd1,4'd1,4'd2,4'd3,4'd4,4'd5,4'd6,4'd7,4'd8,4'd9,4'd9,4'd9,4'd9,4'd9,4'd9,4'd9,4'd9,4'd9,4'd8,4'd7,4'd6,4'd5,4'd4,4'd3,4'd2,4'd2,4'd2,4'd2,4'd2,4'd2,4'd2,4'd2,4'd3,4'd4,4'd5,4'd6,4'd7,4'd8,4'd8,4'd8,4'd8,4'd8,4'd8,4'd8,4'd7,4'd6,4'd5,4'd4,4'd3,4'd3,4'd3,4'd3,4'd3,4'd3,4'd4,4'd5,4'd6,4'd7,4'd7,4'd7,4'd7,4'd7,4'd6,4'd5,4'd4,4'd4,4'd4,4'd4,4'd5,4'd6,4'd6,4'd6,4'd5,4'd5}; + +// DIM_SIZE = 12, PATTERN = SPIRAL +//------------------------------------ +//   110  111  112  113  114  115  116  117  118  119  120  121 +//   109   72   73   74   75   76   77   78   79   80   81  122 +//   108   71   42   43   44   45   46   47   48   49   82  123 +//   107   70   41   20   21   22   23   24   25   50   83  124 +//   106   69   40   19    6    7    8    9   26   51   84  125 +//   105   68   39   18    5    0    1   10   27   52   85  126 +//   104   67   38   17    4    3    2   11   28   53   86  127 +//   103   66   37   16   15   14   13   12   29   54   87  128 +//   102   65   36   35   34   33   32   31   30   55   88  129 +//   101   64   63   62   61   60   59   58   57   56   89  130 +//   100   99   98   97   96   95   94   93   92   91   90  131 +//   143  142  141  140  139  138  137  136  135  134  133  132 +localparam [575:0] XCOORD_DIM_012 = {4'd0,4'd1,4'd2,4'd3,4'd4,4'd5,4'd6,4'd7,4'd8,4'd9,4'd10,4'd11,4'd11,4'd11,4'd11,4'd11,4'd11,4'd11,4'd11,4'd11,4'd11,4'd11,4'd11,4'd10,4'd9,4'd8,4'd7,4'd6,4'd5,4'd4,4'd3,4'd2,4'd1,4'd0,4'd0,4'd0,4'd0,4'd0,4'd0,4'd0,4'd0,4'd0,4'd0,4'd0,4'd1,4'd2,4'd3,4'd4,4'd5,4'd6,4'd7,4'd8,4'd9,4'd10,4'd10,4'd10,4'd10,4'd10,4'd10,4'd10,4'd10,4'd10,4'd10,4'd9,4'd8,4'd7,4'd6,4'd5,4'd4,4'd3,4'd2,4'd1,4'd1,4'd1,4'd1,4'd1,4'd1,4'd1,4'd1,4'd1,4'd2,4'd3,4'd4,4'd5,4'd6,4'd7,4'd8,4'd9,4'd9,4'd9,4'd9,4'd9,4'd9,4'd9,4'd9,4'd8,4'd7,4'd6,4'd5,4'd4,4'd3,4'd2,4'd2,4'd2,4'd2,4'd2,4'd2,4'd2,4'd3,4'd4,4'd5,4'd6,4'd7,4'd8,4'd8,4'd8,4'd8,4'd8,4'd8,4'd7,4'd6,4'd5,4'd4,4'd3,4'd3,4'd3,4'd3,4'd3,4'd4,4'd5,4'd6,4'd7,4'd7,4'd7,4'd7,4'd6,4'd5,4'd4,4'd4,4'd4,4'd5,4'd6,4'd6,4'd5}; +localparam [575:0] YCOORD_DIM_012 = {4'd11,4'd11,4'd11,4'd11,4'd11,4'd11,4'd11,4'd11,4'd11,4'd11,4'd11,4'd11,4'd10,4'd9,4'd8,4'd7,4'd6,4'd5,4'd4,4'd3,4'd2,4'd1,4'd0,4'd0,4'd0,4'd0,4'd0,4'd0,4'd0,4'd0,4'd0,4'd0,4'd0,4'd0,4'd1,4'd2,4'd3,4'd4,4'd5,4'd6,4'd7,4'd8,4'd9,4'd10,4'd10,4'd10,4'd10,4'd10,4'd10,4'd10,4'd10,4'd10,4'd10,4'd10,4'd9,4'd8,4'd7,4'd6,4'd5,4'd4,4'd3,4'd2,4'd1,4'd1,4'd1,4'd1,4'd1,4'd1,4'd1,4'd1,4'd1,4'd1,4'd2,4'd3,4'd4,4'd5,4'd6,4'd7,4'd8,4'd9,4'd9,4'd9,4'd9,4'd9,4'd9,4'd9,4'd9,4'd9,4'd8,4'd7,4'd6,4'd5,4'd4,4'd3,4'd2,4'd2,4'd2,4'd2,4'd2,4'd2,4'd2,4'd2,4'd3,4'd4,4'd5,4'd6,4'd7,4'd8,4'd8,4'd8,4'd8,4'd8,4'd8,4'd8,4'd7,4'd6,4'd5,4'd4,4'd3,4'd3,4'd3,4'd3,4'd3,4'd3,4'd4,4'd5,4'd6,4'd7,4'd7,4'd7,4'd7,4'd7,4'd6,4'd5,4'd4,4'd4,4'd4,4'd4,4'd5,4'd6,4'd6,4'd6,4'd5,4'd5}; + +// DIM_SIZE = 13, PATTERN = SPIRAL +//------------------------------------ +//   156  157  158  159  160  161  162  163  164  165  166  167  168 +//   155  110  111  112  113  114  115  116  117  118  119  120  121 +//   154  109   72   73   74   75   76   77   78   79   80   81  122 +//   153  108   71   42   43   44   45   46   47   48   49   82  123 +//   152  107   70   41   20   21   22   23   24   25   50   83  124 +//   151  106   69   40   19    6    7    8    9   26   51   84  125 +//   150  105   68   39   18    5    0    1   10   27   52   85  126 +//   149  104   67   38   17    4    3    2   11   28   53   86  127 +//   148  103   66   37   16   15   14   13   12   29   54   87  128 +//   147  102   65   36   35   34   33   32   31   30   55   88  129 +//   146  101   64   63   62   61   60   59   58   57   56   89  130 +//   145  100   99   98   97   96   95   94   93   92   91   90  131 +//   144  143  142  141  140  139  138  137  136  135  134  133  132 +localparam [675:0] XCOORD_DIM_013 = {4'd12,4'd11,4'd10,4'd9,4'd8,4'd7,4'd6,4'd5,4'd4,4'd3,4'd2,4'd1,4'd0,4'd0,4'd0,4'd0,4'd0,4'd0,4'd0,4'd0,4'd0,4'd0,4'd0,4'd0,4'd0,4'd1,4'd2,4'd3,4'd4,4'd5,4'd6,4'd7,4'd8,4'd9,4'd10,4'd11,4'd12,4'd12,4'd12,4'd12,4'd12,4'd12,4'd12,4'd12,4'd12,4'd12,4'd12,4'd12,4'd11,4'd10,4'd9,4'd8,4'd7,4'd6,4'd5,4'd4,4'd3,4'd2,4'd1,4'd1,4'd1,4'd1,4'd1,4'd1,4'd1,4'd1,4'd1,4'd1,4'd1,4'd2,4'd3,4'd4,4'd5,4'd6,4'd7,4'd8,4'd9,4'd10,4'd11,4'd11,4'd11,4'd11,4'd11,4'd11,4'd11,4'd11,4'd11,4'd11,4'd10,4'd9,4'd8,4'd7,4'd6,4'd5,4'd4,4'd3,4'd2,4'd2,4'd2,4'd2,4'd2,4'd2,4'd2,4'd2,4'd2,4'd3,4'd4,4'd5,4'd6,4'd7,4'd8,4'd9,4'd10,4'd10,4'd10,4'd10,4'd10,4'd10,4'd10,4'd10,4'd9,4'd8,4'd7,4'd6,4'd5,4'd4,4'd3,4'd3,4'd3,4'd3,4'd3,4'd3,4'd3,4'd4,4'd5,4'd6,4'd7,4'd8,4'd9,4'd9,4'd9,4'd9,4'd9,4'd9,4'd8,4'd7,4'd6,4'd5,4'd4,4'd4,4'd4,4'd4,4'd4,4'd5,4'd6,4'd7,4'd8,4'd8,4'd8,4'd8,4'd7,4'd6,4'd5,4'd5,4'd5,4'd6,4'd7,4'd7,4'd6}; +localparam [675:0] YCOORD_DIM_013 = {4'd0,4'd0,4'd0,4'd0,4'd0,4'd0,4'd0,4'd0,4'd0,4'd0,4'd0,4'd0,4'd0,4'd1,4'd2,4'd3,4'd4,4'd5,4'd6,4'd7,4'd8,4'd9,4'd10,4'd11,4'd12,4'd12,4'd12,4'd12,4'd12,4'd12,4'd12,4'd12,4'd12,4'd12,4'd12,4'd12,4'd12,4'd11,4'd10,4'd9,4'd8,4'd7,4'd6,4'd5,4'd4,4'd3,4'd2,4'd1,4'd1,4'd1,4'd1,4'd1,4'd1,4'd1,4'd1,4'd1,4'd1,4'd1,4'd1,4'd2,4'd3,4'd4,4'd5,4'd6,4'd7,4'd8,4'd9,4'd10,4'd11,4'd11,4'd11,4'd11,4'd11,4'd11,4'd11,4'd11,4'd11,4'd11,4'd11,4'd10,4'd9,4'd8,4'd7,4'd6,4'd5,4'd4,4'd3,4'd2,4'd2,4'd2,4'd2,4'd2,4'd2,4'd2,4'd2,4'd2,4'd2,4'd3,4'd4,4'd5,4'd6,4'd7,4'd8,4'd9,4'd10,4'd10,4'd10,4'd10,4'd10,4'd10,4'd10,4'd10,4'd10,4'd9,4'd8,4'd7,4'd6,4'd5,4'd4,4'd3,4'd3,4'd3,4'd3,4'd3,4'd3,4'd3,4'd3,4'd4,4'd5,4'd6,4'd7,4'd8,4'd9,4'd9,4'd9,4'd9,4'd9,4'd9,4'd9,4'd8,4'd7,4'd6,4'd5,4'd4,4'd4,4'd4,4'd4,4'd4,4'd4,4'd5,4'd6,4'd7,4'd8,4'd8,4'd8,4'd8,4'd8,4'd7,4'd6,4'd5,4'd5,4'd5,4'd5,4'd6,4'd7,4'd7,4'd7,4'd6,4'd6}; + +// DIM_SIZE = 14, PATTERN = SPIRAL +//------------------------------------ +//   156  157  158  159  160  161  162  163  164  165  166  167  168  169 +//   155  110  111  112  113  114  115  116  117  118  119  120  121  170 +//   154  109   72   73   74   75   76   77   78   79   80   81  122  171 +//   153  108   71   42   43   44   45   46   47   48   49   82  123  172 +//   152  107   70   41   20   21   22   23   24   25   50   83  124  173 +//   151  106   69   40   19    6    7    8    9   26   51   84  125  174 +//   150  105   68   39   18    5    0    1   10   27   52   85  126  175 +//   149  104   67   38   17    4    3    2   11   28   53   86  127  176 +//   148  103   66   37   16   15   14   13   12   29   54   87  128  177 +//   147  102   65   36   35   34   33   32   31   30   55   88  129  178 +//   146  101   64   63   62   61   60   59   58   57   56   89  130  179 +//   145  100   99   98   97   96   95   94   93   92   91   90  131  180 +//   144  143  142  141  140  139  138  137  136  135  134  133  132  181 +//   195  194  193  192  191  190  189  188  187  186  185  184  183  182 +localparam [783:0] XCOORD_DIM_014 = {4'd0,4'd1,4'd2,4'd3,4'd4,4'd5,4'd6,4'd7,4'd8,4'd9,4'd10,4'd11,4'd12,4'd13,4'd13,4'd13,4'd13,4'd13,4'd13,4'd13,4'd13,4'd13,4'd13,4'd13,4'd13,4'd13,4'd13,4'd12,4'd11,4'd10,4'd9,4'd8,4'd7,4'd6,4'd5,4'd4,4'd3,4'd2,4'd1,4'd0,4'd0,4'd0,4'd0,4'd0,4'd0,4'd0,4'd0,4'd0,4'd0,4'd0,4'd0,4'd0,4'd1,4'd2,4'd3,4'd4,4'd5,4'd6,4'd7,4'd8,4'd9,4'd10,4'd11,4'd12,4'd12,4'd12,4'd12,4'd12,4'd12,4'd12,4'd12,4'd12,4'd12,4'd12,4'd12,4'd11,4'd10,4'd9,4'd8,4'd7,4'd6,4'd5,4'd4,4'd3,4'd2,4'd1,4'd1,4'd1,4'd1,4'd1,4'd1,4'd1,4'd1,4'd1,4'd1,4'd1,4'd2,4'd3,4'd4,4'd5,4'd6,4'd7,4'd8,4'd9,4'd10,4'd11,4'd11,4'd11,4'd11,4'd11,4'd11,4'd11,4'd11,4'd11,4'd11,4'd10,4'd9,4'd8,4'd7,4'd6,4'd5,4'd4,4'd3,4'd2,4'd2,4'd2,4'd2,4'd2,4'd2,4'd2,4'd2,4'd2,4'd3,4'd4,4'd5,4'd6,4'd7,4'd8,4'd9,4'd10,4'd10,4'd10,4'd10,4'd10,4'd10,4'd10,4'd10,4'd9,4'd8,4'd7,4'd6,4'd5,4'd4,4'd3,4'd3,4'd3,4'd3,4'd3,4'd3,4'd3,4'd4,4'd5,4'd6,4'd7,4'd8,4'd9,4'd9,4'd9,4'd9,4'd9,4'd9,4'd8,4'd7,4'd6,4'd5,4'd4,4'd4,4'd4,4'd4,4'd4,4'd5,4'd6,4'd7,4'd8,4'd8,4'd8,4'd8,4'd7,4'd6,4'd5,4'd5,4'd5,4'd6,4'd7,4'd7,4'd6}; +localparam [783:0] YCOORD_DIM_014 = {4'd13,4'd13,4'd13,4'd13,4'd13,4'd13,4'd13,4'd13,4'd13,4'd13,4'd13,4'd13,4'd13,4'd13,4'd12,4'd11,4'd10,4'd9,4'd8,4'd7,4'd6,4'd5,4'd4,4'd3,4'd2,4'd1,4'd0,4'd0,4'd0,4'd0,4'd0,4'd0,4'd0,4'd0,4'd0,4'd0,4'd0,4'd0,4'd0,4'd0,4'd1,4'd2,4'd3,4'd4,4'd5,4'd6,4'd7,4'd8,4'd9,4'd10,4'd11,4'd12,4'd12,4'd12,4'd12,4'd12,4'd12,4'd12,4'd12,4'd12,4'd12,4'd12,4'd12,4'd12,4'd11,4'd10,4'd9,4'd8,4'd7,4'd6,4'd5,4'd4,4'd3,4'd2,4'd1,4'd1,4'd1,4'd1,4'd1,4'd1,4'd1,4'd1,4'd1,4'd1,4'd1,4'd1,4'd2,4'd3,4'd4,4'd5,4'd6,4'd7,4'd8,4'd9,4'd10,4'd11,4'd11,4'd11,4'd11,4'd11,4'd11,4'd11,4'd11,4'd11,4'd11,4'd11,4'd10,4'd9,4'd8,4'd7,4'd6,4'd5,4'd4,4'd3,4'd2,4'd2,4'd2,4'd2,4'd2,4'd2,4'd2,4'd2,4'd2,4'd2,4'd3,4'd4,4'd5,4'd6,4'd7,4'd8,4'd9,4'd10,4'd10,4'd10,4'd10,4'd10,4'd10,4'd10,4'd10,4'd10,4'd9,4'd8,4'd7,4'd6,4'd5,4'd4,4'd3,4'd3,4'd3,4'd3,4'd3,4'd3,4'd3,4'd3,4'd4,4'd5,4'd6,4'd7,4'd8,4'd9,4'd9,4'd9,4'd9,4'd9,4'd9,4'd9,4'd8,4'd7,4'd6,4'd5,4'd4,4'd4,4'd4,4'd4,4'd4,4'd4,4'd5,4'd6,4'd7,4'd8,4'd8,4'd8,4'd8,4'd8,4'd7,4'd6,4'd5,4'd5,4'd5,4'd5,4'd6,4'd7,4'd7,4'd7,4'd6,4'd6}; + +// DIM_SIZE = 15, PATTERN = SPIRAL +//------------------------------------ +//   210  211  212  213  214  215  216  217  218  219  220  221  222  223  224 +//   209  156  157  158  159  160  161  162  163  164  165  166  167  168  169 +//   208  155  110  111  112  113  114  115  116  117  118  119  120  121  170 +//   207  154  109   72   73   74   75   76   77   78   79   80   81  122  171 +//   206  153  108   71   42   43   44   45   46   47   48   49   82  123  172 +//   205  152  107   70   41   20   21   22   23   24   25   50   83  124  173 +//   204  151  106   69   40   19    6    7    8    9   26   51   84  125  174 +//   203  150  105   68   39   18    5    0    1   10   27   52   85  126  175 +//   202  149  104   67   38   17    4    3    2   11   28   53   86  127  176 +//   201  148  103   66   37   16   15   14   13   12   29   54   87  128  177 +//   200  147  102   65   36   35   34   33   32   31   30   55   88  129  178 +//   199  146  101   64   63   62   61   60   59   58   57   56   89  130  179 +//   198  145  100   99   98   97   96   95   94   93   92   91   90  131  180 +//   197  144  143  142  141  140  139  138  137  136  135  134  133  132  181 +//   196  195  194  193  192  191  190  189  188  187  186  185  184  183  182 +localparam [899:0] XCOORD_DIM_015 = {4'd14,4'd13,4'd12,4'd11,4'd10,4'd9,4'd8,4'd7,4'd6,4'd5,4'd4,4'd3,4'd2,4'd1,4'd0,4'd0,4'd0,4'd0,4'd0,4'd0,4'd0,4'd0,4'd0,4'd0,4'd0,4'd0,4'd0,4'd0,4'd0,4'd1,4'd2,4'd3,4'd4,4'd5,4'd6,4'd7,4'd8,4'd9,4'd10,4'd11,4'd12,4'd13,4'd14,4'd14,4'd14,4'd14,4'd14,4'd14,4'd14,4'd14,4'd14,4'd14,4'd14,4'd14,4'd14,4'd14,4'd13,4'd12,4'd11,4'd10,4'd9,4'd8,4'd7,4'd6,4'd5,4'd4,4'd3,4'd2,4'd1,4'd1,4'd1,4'd1,4'd1,4'd1,4'd1,4'd1,4'd1,4'd1,4'd1,4'd1,4'd1,4'd2,4'd3,4'd4,4'd5,4'd6,4'd7,4'd8,4'd9,4'd10,4'd11,4'd12,4'd13,4'd13,4'd13,4'd13,4'd13,4'd13,4'd13,4'd13,4'd13,4'd13,4'd13,4'd13,4'd12,4'd11,4'd10,4'd9,4'd8,4'd7,4'd6,4'd5,4'd4,4'd3,4'd2,4'd2,4'd2,4'd2,4'd2,4'd2,4'd2,4'd2,4'd2,4'd2,4'd2,4'd3,4'd4,4'd5,4'd6,4'd7,4'd8,4'd9,4'd10,4'd11,4'd12,4'd12,4'd12,4'd12,4'd12,4'd12,4'd12,4'd12,4'd12,4'd12,4'd11,4'd10,4'd9,4'd8,4'd7,4'd6,4'd5,4'd4,4'd3,4'd3,4'd3,4'd3,4'd3,4'd3,4'd3,4'd3,4'd3,4'd4,4'd5,4'd6,4'd7,4'd8,4'd9,4'd10,4'd11,4'd11,4'd11,4'd11,4'd11,4'd11,4'd11,4'd11,4'd10,4'd9,4'd8,4'd7,4'd6,4'd5,4'd4,4'd4,4'd4,4'd4,4'd4,4'd4,4'd4,4'd5,4'd6,4'd7,4'd8,4'd9,4'd10,4'd10,4'd10,4'd10,4'd10,4'd10,4'd9,4'd8,4'd7,4'd6,4'd5,4'd5,4'd5,4'd5,4'd5,4'd6,4'd7,4'd8,4'd9,4'd9,4'd9,4'd9,4'd8,4'd7,4'd6,4'd6,4'd6,4'd7,4'd8,4'd8,4'd7}; +localparam [899:0] YCOORD_DIM_015 = {4'd0,4'd0,4'd0,4'd0,4'd0,4'd0,4'd0,4'd0,4'd0,4'd0,4'd0,4'd0,4'd0,4'd0,4'd0,4'd1,4'd2,4'd3,4'd4,4'd5,4'd6,4'd7,4'd8,4'd9,4'd10,4'd11,4'd12,4'd13,4'd14,4'd14,4'd14,4'd14,4'd14,4'd14,4'd14,4'd14,4'd14,4'd14,4'd14,4'd14,4'd14,4'd14,4'd14,4'd13,4'd12,4'd11,4'd10,4'd9,4'd8,4'd7,4'd6,4'd5,4'd4,4'd3,4'd2,4'd1,4'd1,4'd1,4'd1,4'd1,4'd1,4'd1,4'd1,4'd1,4'd1,4'd1,4'd1,4'd1,4'd1,4'd2,4'd3,4'd4,4'd5,4'd6,4'd7,4'd8,4'd9,4'd10,4'd11,4'd12,4'd13,4'd13,4'd13,4'd13,4'd13,4'd13,4'd13,4'd13,4'd13,4'd13,4'd13,4'd13,4'd13,4'd12,4'd11,4'd10,4'd9,4'd8,4'd7,4'd6,4'd5,4'd4,4'd3,4'd2,4'd2,4'd2,4'd2,4'd2,4'd2,4'd2,4'd2,4'd2,4'd2,4'd2,4'd2,4'd3,4'd4,4'd5,4'd6,4'd7,4'd8,4'd9,4'd10,4'd11,4'd12,4'd12,4'd12,4'd12,4'd12,4'd12,4'd12,4'd12,4'd12,4'd12,4'd12,4'd11,4'd10,4'd9,4'd8,4'd7,4'd6,4'd5,4'd4,4'd3,4'd3,4'd3,4'd3,4'd3,4'd3,4'd3,4'd3,4'd3,4'd3,4'd4,4'd5,4'd6,4'd7,4'd8,4'd9,4'd10,4'd11,4'd11,4'd11,4'd11,4'd11,4'd11,4'd11,4'd11,4'd11,4'd10,4'd9,4'd8,4'd7,4'd6,4'd5,4'd4,4'd4,4'd4,4'd4,4'd4,4'd4,4'd4,4'd4,4'd5,4'd6,4'd7,4'd8,4'd9,4'd10,4'd10,4'd10,4'd10,4'd10,4'd10,4'd10,4'd9,4'd8,4'd7,4'd6,4'd5,4'd5,4'd5,4'd5,4'd5,4'd5,4'd6,4'd7,4'd8,4'd9,4'd9,4'd9,4'd9,4'd9,4'd8,4'd7,4'd6,4'd6,4'd6,4'd6,4'd7,4'd8,4'd8,4'd8,4'd7,4'd7}; + +// DIM_SIZE = 16, PATTERN = SPIRAL +//------------------------------------ +//   210  211  212  213  214  215  216  217  218  219  220  221  222  223  224  225 +//   209  156  157  158  159  160  161  162  163  164  165  166  167  168  169  226 +//   208  155  110  111  112  113  114  115  116  117  118  119  120  121  170  227 +//   207  154  109   72   73   74   75   76   77   78   79   80   81  122  171  228 +//   206  153  108   71   42   43   44   45   46   47   48   49   82  123  172  229 +//   205  152  107   70   41   20   21   22   23   24   25   50   83  124  173  230 +//   204  151  106   69   40   19    6    7    8    9   26   51   84  125  174  231 +//   203  150  105   68   39   18    5    0    1   10   27   52   85  126  175  232 +//   202  149  104   67   38   17    4    3    2   11   28   53   86  127  176  233 +//   201  148  103   66   37   16   15   14   13   12   29   54   87  128  177  234 +//   200  147  102   65   36   35   34   33   32   31   30   55   88  129  178  235 +//   199  146  101   64   63   62   61   60   59   58   57   56   89  130  179  236 +//   198  145  100   99   98   97   96   95   94   93   92   91   90  131  180  237 +//   197  144  143  142  141  140  139  138  137  136  135  134  133  132  181  238 +//   196  195  194  193  192  191  190  189  188  187  186  185  184  183  182  239 +//   255  254  253  252  251  250  249  248  247  246  245  244  243  242  241  240 +localparam [1023:0] XCOORD_DIM_016 = {4'd0,4'd1,4'd2,4'd3,4'd4,4'd5,4'd6,4'd7,4'd8,4'd9,4'd10,4'd11,4'd12,4'd13,4'd14,4'd15,4'd15,4'd15,4'd15,4'd15,4'd15,4'd15,4'd15,4'd15,4'd15,4'd15,4'd15,4'd15,4'd15,4'd15,4'd15,4'd14,4'd13,4'd12,4'd11,4'd10,4'd9,4'd8,4'd7,4'd6,4'd5,4'd4,4'd3,4'd2,4'd1,4'd0,4'd0,4'd0,4'd0,4'd0,4'd0,4'd0,4'd0,4'd0,4'd0,4'd0,4'd0,4'd0,4'd0,4'd0,4'd1,4'd2,4'd3,4'd4,4'd5,4'd6,4'd7,4'd8,4'd9,4'd10,4'd11,4'd12,4'd13,4'd14,4'd14,4'd14,4'd14,4'd14,4'd14,4'd14,4'd14,4'd14,4'd14,4'd14,4'd14,4'd14,4'd14,4'd13,4'd12,4'd11,4'd10,4'd9,4'd8,4'd7,4'd6,4'd5,4'd4,4'd3,4'd2,4'd1,4'd1,4'd1,4'd1,4'd1,4'd1,4'd1,4'd1,4'd1,4'd1,4'd1,4'd1,4'd1,4'd2,4'd3,4'd4,4'd5,4'd6,4'd7,4'd8,4'd9,4'd10,4'd11,4'd12,4'd13,4'd13,4'd13,4'd13,4'd13,4'd13,4'd13,4'd13,4'd13,4'd13,4'd13,4'd13,4'd12,4'd11,4'd10,4'd9,4'd8,4'd7,4'd6,4'd5,4'd4,4'd3,4'd2,4'd2,4'd2,4'd2,4'd2,4'd2,4'd2,4'd2,4'd2,4'd2,4'd2,4'd3,4'd4,4'd5,4'd6,4'd7,4'd8,4'd9,4'd10,4'd11,4'd12,4'd12,4'd12,4'd12,4'd12,4'd12,4'd12,4'd12,4'd12,4'd12,4'd11,4'd10,4'd9,4'd8,4'd7,4'd6,4'd5,4'd4,4'd3,4'd3,4'd3,4'd3,4'd3,4'd3,4'd3,4'd3,4'd3,4'd4,4'd5,4'd6,4'd7,4'd8,4'd9,4'd10,4'd11,4'd11,4'd11,4'd11,4'd11,4'd11,4'd11,4'd11,4'd10,4'd9,4'd8,4'd7,4'd6,4'd5,4'd4,4'd4,4'd4,4'd4,4'd4,4'd4,4'd4,4'd5,4'd6,4'd7,4'd8,4'd9,4'd10,4'd10,4'd10,4'd10,4'd10,4'd10,4'd9,4'd8,4'd7,4'd6,4'd5,4'd5,4'd5,4'd5,4'd5,4'd6,4'd7,4'd8,4'd9,4'd9,4'd9,4'd9,4'd8,4'd7,4'd6,4'd6,4'd6,4'd7,4'd8,4'd8,4'd7}; +localparam [1023:0] YCOORD_DIM_016 = {4'd15,4'd15,4'd15,4'd15,4'd15,4'd15,4'd15,4'd15,4'd15,4'd15,4'd15,4'd15,4'd15,4'd15,4'd15,4'd15,4'd14,4'd13,4'd12,4'd11,4'd10,4'd9,4'd8,4'd7,4'd6,4'd5,4'd4,4'd3,4'd2,4'd1,4'd0,4'd0,4'd0,4'd0,4'd0,4'd0,4'd0,4'd0,4'd0,4'd0,4'd0,4'd0,4'd0,4'd0,4'd0,4'd0,4'd1,4'd2,4'd3,4'd4,4'd5,4'd6,4'd7,4'd8,4'd9,4'd10,4'd11,4'd12,4'd13,4'd14,4'd14,4'd14,4'd14,4'd14,4'd14,4'd14,4'd14,4'd14,4'd14,4'd14,4'd14,4'd14,4'd14,4'd14,4'd13,4'd12,4'd11,4'd10,4'd9,4'd8,4'd7,4'd6,4'd5,4'd4,4'd3,4'd2,4'd1,4'd1,4'd1,4'd1,4'd1,4'd1,4'd1,4'd1,4'd1,4'd1,4'd1,4'd1,4'd1,4'd1,4'd2,4'd3,4'd4,4'd5,4'd6,4'd7,4'd8,4'd9,4'd10,4'd11,4'd12,4'd13,4'd13,4'd13,4'd13,4'd13,4'd13,4'd13,4'd13,4'd13,4'd13,4'd13,4'd13,4'd13,4'd12,4'd11,4'd10,4'd9,4'd8,4'd7,4'd6,4'd5,4'd4,4'd3,4'd2,4'd2,4'd2,4'd2,4'd2,4'd2,4'd2,4'd2,4'd2,4'd2,4'd2,4'd2,4'd3,4'd4,4'd5,4'd6,4'd7,4'd8,4'd9,4'd10,4'd11,4'd12,4'd12,4'd12,4'd12,4'd12,4'd12,4'd12,4'd12,4'd12,4'd12,4'd12,4'd11,4'd10,4'd9,4'd8,4'd7,4'd6,4'd5,4'd4,4'd3,4'd3,4'd3,4'd3,4'd3,4'd3,4'd3,4'd3,4'd3,4'd3,4'd4,4'd5,4'd6,4'd7,4'd8,4'd9,4'd10,4'd11,4'd11,4'd11,4'd11,4'd11,4'd11,4'd11,4'd11,4'd11,4'd10,4'd9,4'd8,4'd7,4'd6,4'd5,4'd4,4'd4,4'd4,4'd4,4'd4,4'd4,4'd4,4'd4,4'd5,4'd6,4'd7,4'd8,4'd9,4'd10,4'd10,4'd10,4'd10,4'd10,4'd10,4'd10,4'd9,4'd8,4'd7,4'd6,4'd5,4'd5,4'd5,4'd5,4'd5,4'd5,4'd6,4'd7,4'd8,4'd9,4'd9,4'd9,4'd9,4'd9,4'd8,4'd7,4'd6,4'd6,4'd6,4'd6,4'd7,4'd8,4'd8,4'd8,4'd7,4'd7}; + + +function [CLOG2_DIM_SIZE-1:0] node_to_xdst; +  input [WIDTH-1:0] header; +begin +  if (DIM_SIZE == 2) +    node_to_xdst = XCOORD_DIM_002[1*header[1:0] +: 1]; +  else if (DIM_SIZE == 3) +    node_to_xdst = XCOORD_DIM_003[2*header[3:0] +: 2]; +  else if (DIM_SIZE == 4) +    node_to_xdst = XCOORD_DIM_004[2*header[3:0] +: 2]; +  else if (DIM_SIZE == 5) +    node_to_xdst = XCOORD_DIM_005[3*header[4:0] +: 3]; +  else if (DIM_SIZE == 6) +    node_to_xdst = XCOORD_DIM_006[3*header[5:0] +: 3]; +  else if (DIM_SIZE == 7) +    node_to_xdst = XCOORD_DIM_007[3*header[5:0] +: 3]; +  else if (DIM_SIZE == 8) +    node_to_xdst = XCOORD_DIM_008[3*header[5:0] +: 3]; +  else if (DIM_SIZE == 9) +    node_to_xdst = XCOORD_DIM_009[4*header[6:0] +: 4]; +  else if (DIM_SIZE == 10) +    node_to_xdst = XCOORD_DIM_010[4*header[6:0] +: 4]; +  else if (DIM_SIZE == 11) +    node_to_xdst = XCOORD_DIM_011[4*header[6:0] +: 4]; +  else if (DIM_SIZE == 12) +    node_to_xdst = XCOORD_DIM_012[4*header[7:0] +: 4]; +  else if (DIM_SIZE == 13) +    node_to_xdst = XCOORD_DIM_013[4*header[7:0] +: 4]; +  else if (DIM_SIZE == 14) +    node_to_xdst = XCOORD_DIM_014[4*header[7:0] +: 4]; +  else if (DIM_SIZE == 15) +    node_to_xdst = XCOORD_DIM_015[4*header[7:0] +: 4]; +  else if (DIM_SIZE == 16) +    node_to_xdst = XCOORD_DIM_016[4*header[7:0] +: 4]; +  else +    node_to_xdst = {CLOG2_DIM_SIZE{1'd0}}; +end endfunction + +function [CLOG2_DIM_SIZE-1:0] node_to_ydst; +  input [WIDTH-1:0] header; +begin +  if (DIM_SIZE == 2) +    node_to_ydst = YCOORD_DIM_002[1*header[1:0] +: 1]; +  else if (DIM_SIZE == 3) +    node_to_ydst = YCOORD_DIM_003[2*header[3:0] +: 2]; +  else if (DIM_SIZE == 4) +    node_to_ydst = YCOORD_DIM_004[2*header[3:0] +: 2]; +  else if (DIM_SIZE == 5) +    node_to_ydst = YCOORD_DIM_005[3*header[4:0] +: 3]; +  else if (DIM_SIZE == 6) +    node_to_ydst = YCOORD_DIM_006[3*header[5:0] +: 3]; +  else if (DIM_SIZE == 7) +    node_to_ydst = YCOORD_DIM_007[3*header[5:0] +: 3]; +  else if (DIM_SIZE == 8) +    node_to_ydst = YCOORD_DIM_008[3*header[5:0] +: 3]; +  else if (DIM_SIZE == 9) +    node_to_ydst = YCOORD_DIM_009[4*header[6:0] +: 4]; +  else if (DIM_SIZE == 10) +    node_to_ydst = YCOORD_DIM_010[4*header[6:0] +: 4]; +  else if (DIM_SIZE == 11) +    node_to_ydst = YCOORD_DIM_011[4*header[6:0] +: 4]; +  else if (DIM_SIZE == 12) +    node_to_ydst = YCOORD_DIM_012[4*header[7:0] +: 4]; +  else if (DIM_SIZE == 13) +    node_to_ydst = YCOORD_DIM_013[4*header[7:0] +: 4]; +  else if (DIM_SIZE == 14) +    node_to_ydst = YCOORD_DIM_014[4*header[7:0] +: 4]; +  else if (DIM_SIZE == 15) +    node_to_ydst = YCOORD_DIM_015[4*header[7:0] +: 4]; +  else if (DIM_SIZE == 16) +    node_to_ydst = YCOORD_DIM_016[4*header[7:0] +: 4]; +  else +    node_to_ydst = {CLOG2_DIM_SIZE{1'd0}}; +end endfunction + diff --git a/fpga/usrp3/lib/rfnoc/crossbar/synth/axis_ctrl_crossbar_nxn_top.tcl b/fpga/usrp3/lib/rfnoc/crossbar/synth/axis_ctrl_crossbar_nxn_top.tcl new file mode 100644 index 000000000..39440b512 --- /dev/null +++ b/fpga/usrp3/lib/rfnoc/crossbar/synth/axis_ctrl_crossbar_nxn_top.tcl @@ -0,0 +1,18 @@ +#!/usr/bin/python3 +# +# Copyright 2018 Ettus Research, a National Instruments Company +# +# SPDX-License-Identifier: LGPL-3.0-or-later +# + +create_project tmp_proj -part xc7k410tffg900-3 -in_memory +add_files {axis_ctrl_crossbar_nxn_top.v ../axis_ctrl_crossbar_nxn.v ../axis_ctrl_crossbar_2d_mesh.v ../mesh_2d_dor_router_multi_sw.v ../axis_switch.v ../axis_ingress_vc_buff.v ../mesh_node_mapping.vh ../mesh_2d_dor_router_single_sw.v ../torus_2d_dor_router_single_sw.v ../torus_2d_dor_router_multi_sw.v ../axis_port_terminator.v} +add_files {../../../fifo/axi_fifo_flop.v ../../../fifo/axi_fifo_flop2.v ../../../fifo/axi_fifo.v ../../../fifo/axi_mux_select.v ../../../fifo/axi_fifo_bram.v ../../../fifo/axi_fifo_cascade.v ../../../fifo/axi_mux.v ../../../fifo/axi_fifo_short.v ../../../fifo/axi_demux.v ../../../fifo/axi_packet_gate.v ../../../control/map/cam_priority_encoder.v ../../../control/map/cam_srl.v ../../../control/map/cam_bram.v ../../../control/map/cam.v ../../../control/map/kv_map.v ../../../control/map/axis_muxed_kv_map.v ../../../control/ram_2port.v} +set_property top axis_ctrl_crossbar_nxn_top [current_fileset] +synth_design +create_clock -name clk -period 2.0 [get_ports clk] +report_utilization -no_primitives -file axis_ctrl_crossbar_nxn.rpt +report_timing_summary -setup -no_detailed_paths -no_header -datasheet -append -file axis_ctrl_crossbar_nxn.rpt +write_checkpoint -force axis_ctrl_crossbar_nxn.dcp +close_project +exit
\ No newline at end of file diff --git a/fpga/usrp3/lib/rfnoc/crossbar/synth/axis_ctrl_crossbar_nxn_top.v.in b/fpga/usrp3/lib/rfnoc/crossbar/synth/axis_ctrl_crossbar_nxn_top.v.in new file mode 100644 index 000000000..6805100b9 --- /dev/null +++ b/fpga/usrp3/lib/rfnoc/crossbar/synth/axis_ctrl_crossbar_nxn_top.v.in @@ -0,0 +1,47 @@ +// +// Copyright 2018 Ettus Research, A National Instruments Company +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// + +module axis_ctrl_crossbar_nxn_top( +  input clk, +  input rst +); +  // Router global config +  localparam IMPL        = "{top}"; +  localparam NPORTS      = {ports}; +  localparam DWIDTH      = {dataw}; +  localparam MTU         = {mtu}; +  localparam ROUTING     = "{ralloc}"; + +  (* dont_touch = "true"*) wire [(DWIDTH*NPORTS)-1:0] s_axis_tdata , m_axis_tdata ; +  (* dont_touch = "true"*) wire [NPORTS-1:0]          s_axis_tlast , m_axis_tlast ; +  (* dont_touch = "true"*) wire [NPORTS-1:0]          s_axis_tvalid, m_axis_tvalid; +  (* dont_touch = "true"*) wire [NPORTS-1:0]          s_axis_tready, m_axis_tready; +  (* dont_touch = "true"*) wire                       deadlock_detected; + +  axis_ctrl_crossbar_nxn #( +    .WIDTH            (DWIDTH), +    .NPORTS           (NPORTS), +    .TOPOLOGY         (IMPL), +    .INGRESS_BUFF_SIZE(MTU), +    .ROUTER_BUFF_SIZE (MTU), +    .ROUTING_ALLOC    (ROUTING), +    .SWITCH_ALLOC     ("ROUND-ROBIN") +  ) router_dut_i ( +    .clk              (clk), +    .reset            (rst), +    .s_axis_tdata     (s_axis_tdata ), +    .s_axis_tlast     (s_axis_tlast ), +    .s_axis_tvalid    (s_axis_tvalid), +    .s_axis_tready    (s_axis_tready), +    .m_axis_tdata     (m_axis_tdata ), +    .m_axis_tlast     (m_axis_tlast ), +    .m_axis_tvalid    (m_axis_tvalid), +    .m_axis_tready    (m_axis_tready), +    .deadlock_detected(deadlock_detected) +  ); + +endmodule + diff --git a/fpga/usrp3/lib/rfnoc/crossbar/synth/chdr_crossbar_nxn_top.tcl b/fpga/usrp3/lib/rfnoc/crossbar/synth/chdr_crossbar_nxn_top.tcl new file mode 100644 index 000000000..304384aee --- /dev/null +++ b/fpga/usrp3/lib/rfnoc/crossbar/synth/chdr_crossbar_nxn_top.tcl @@ -0,0 +1,18 @@ +#!/usr/bin/python3 +# +# Copyright 2018 Ettus Research, a National Instruments Company +# +# SPDX-License-Identifier: LGPL-3.0-or-later +# + +create_project tmp_proj -part xc7k410tffg900-3 -in_memory +add_files {chdr_crossbar_nxn_top.v ../chdr_crossbar_nxn.v ../axis_switch.v ../chdr_xb_ingress_buff.v ../chdr_xb_routing_table.v ../../core/chdr_mgmt_pkt_handler.v ../../core/rfnoc_chdr_utils.vh ../../core/rfnoc_chdr_internal_utils.vh} +add_files {../../../fifo/axi_fifo_flop.v ../../../fifo/axi_fifo_flop2.v ../../../fifo/axi_fifo.v ../../../fifo/axi_mux_select.v ../../../fifo/axi_fifo_bram.v ../../../fifo/axi_fifo_cascade.v ../../../fifo/axi_mux.v ../../../fifo/axi_fifo_short.v ../../../fifo/axi_demux.v ../../../fifo/axi_packet_gate.v ../../../control/map/cam_priority_encoder.v ../../../control/map/cam_srl.v ../../../control/map/cam_bram.v ../../../control/map/cam.v ../../../control/map/kv_map.v ../../../control/map/axis_muxed_kv_map.v ../../../control/ram_2port.v} +set_property top chdr_crossbar_nxn_top [current_fileset] +synth_design +create_clock -name clk -period 2.0 [get_ports clk] +report_utilization -no_primitives -file chdr_crossbar_nxn.rpt +report_timing_summary -setup -no_detailed_paths -no_header -datasheet -append -file chdr_crossbar_nxn.rpt +write_checkpoint -force chdr_crossbar_nxn.dcp +close_project +exit
\ No newline at end of file diff --git a/fpga/usrp3/lib/rfnoc/crossbar/synth/chdr_crossbar_nxn_top.v.in b/fpga/usrp3/lib/rfnoc/crossbar/synth/chdr_crossbar_nxn_top.v.in new file mode 100644 index 000000000..fbf0852a3 --- /dev/null +++ b/fpga/usrp3/lib/rfnoc/crossbar/synth/chdr_crossbar_nxn_top.v.in @@ -0,0 +1,55 @@ +// +// Copyright 2018 Ettus Research, A National Instruments Company +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// + +module chdr_crossbar_nxn_top( +  input clk, +  input rst +); +  // Router global config +  localparam NPORTS      = {ports}; +  localparam DWIDTH      = {dataw}; +  localparam MTU         = {mtu}; +  localparam RLUT_SIZE   = {rlutsize}; +  localparam OPTIMIZE    = "{opt}"; + +  (* dont_touch = "true"*) wire [(DWIDTH*NPORTS)-1:0] s_axis_tdata , m_axis_tdata ; +  (* dont_touch = "true"*) wire [NPORTS-1:0]          s_axis_tlast , m_axis_tlast ; +  (* dont_touch = "true"*) wire [NPORTS-1:0]          s_axis_tvalid, m_axis_tvalid; +  (* dont_touch = "true"*) wire [NPORTS-1:0]          s_axis_tready, m_axis_tready; + +  chdr_crossbar_nxn #( +    .CHDR_W             (DWIDTH), +    .NPORTS             (NPORTS), +    .DEFAULT_PORT       (0), +    .MTU                (MTU), +    .ROUTE_TBL_SIZE     (RLUT_SIZE), +    .MUX_ALLOC          ("ROUND-ROBIN"), +    .OPTIMIZE           (OPTIMIZE), +    .NPORTS_MGMT        (NPORTS), +    .EXT_RTCFG_PORT     (1) +  ) router_dut_i ( +     // General          +    .clk                (clk), +    .reset              (rst), +    // Inputs            +    .s_axis_tdata       (s_axis_tdata), +    .s_axis_tlast       (s_axis_tlast), +    .s_axis_tvalid      (s_axis_tvalid), +    .s_axis_tready      (s_axis_tready), +    // Output            +    .m_axis_tdata       (m_axis_tdata), +    .m_axis_tlast       (m_axis_tlast), +    .m_axis_tvalid      (m_axis_tvalid), +    .m_axis_tready      (m_axis_tready), +    // External rtcfg port +    .ext_rtcfg_stb      (0), +    .ext_rtcfg_addr     (0), +    .ext_rtcfg_data     (0), +    .ext_rtcfg_ack      () +  ); + +endmodule + diff --git a/fpga/usrp3/lib/rfnoc/crossbar/synth/synth_axis_ctrl_crossbar_nxn.py b/fpga/usrp3/lib/rfnoc/crossbar/synth/synth_axis_ctrl_crossbar_nxn.py new file mode 100755 index 000000000..4ca6e07fa --- /dev/null +++ b/fpga/usrp3/lib/rfnoc/crossbar/synth/synth_axis_ctrl_crossbar_nxn.py @@ -0,0 +1,37 @@ +#! /usr/bin/python3 +#!/usr/bin/python3 +# +# Copyright 2018 Ettus Research, a National Instruments Company +# +# SPDX-License-Identifier: LGPL-3.0-or-later +# + +import argparse +import synth_run + +modname = 'axis_ctrl_crossbar_nxn' + +# Parse command line options +def get_options(): +    parser = argparse.ArgumentParser(description='Generate synthesis results for ' + modname) +    parser.add_argument('--top', type=str, default='TORUS', help='Topologies (CSV)') +    parser.add_argument('--ports', type=str, default='8', help='Number of ports (CSV)') +    parser.add_argument('--dataw', type=str, default='32', help='Router datapath width (CSV)') +    parser.add_argument('--mtu', type=str, default='5', help='MTU (CSV)') +    parser.add_argument('--ralloc', type=str, default='WORMHOLE', help='Router allocation method (CSV)') +    return parser.parse_args() + +def main(): +    args = get_options() +    keys = ['top', 'ports', 'dataw', 'mtu', 'ralloc'] +    for top in args.top.strip().split(','): +        for ports in args.ports.strip().split(','): +            for dataw in args.dataw.strip().split(','): +                for mtu in args.mtu.strip().split(','): +                    for ralloc in args.ralloc.strip().split(','): +                        # Collect parameters +                        transform = {'ports':ports, 'dataw':dataw, 'mtu':mtu, 'top':top, 'ralloc':ralloc} +                        synth_run.synth_run(modname, keys, transform) + +if __name__ == '__main__': +    main() diff --git a/fpga/usrp3/lib/rfnoc/crossbar/synth/synth_chdr_crossbar_nxn.py b/fpga/usrp3/lib/rfnoc/crossbar/synth/synth_chdr_crossbar_nxn.py new file mode 100755 index 000000000..668e7a247 --- /dev/null +++ b/fpga/usrp3/lib/rfnoc/crossbar/synth/synth_chdr_crossbar_nxn.py @@ -0,0 +1,37 @@ +#! /usr/bin/python3 +#!/usr/bin/python3 +# +# Copyright 2018 Ettus Research, a National Instruments Company +# +# SPDX-License-Identifier: LGPL-3.0-or-later +# + +import argparse +import synth_run + +modname = 'chdr_crossbar_nxn' + +# Parse command line options +def get_options(): +    parser = argparse.ArgumentParser(description='Generate synthesis results for ' + modname) +    parser.add_argument('--opt', type=str, default='AREA', help='Optimization strategies (CSV)') +    parser.add_argument('--ports', type=str, default='8', help='Number of ports (CSV)') +    parser.add_argument('--dataw', type=str, default='64', help='Router datapath width (CSV)') +    parser.add_argument('--mtu', type=str, default='10', help='MTU or Ingress buffer size (CSV)') +    parser.add_argument('--rlutsize', type=str, default='6', help='Router lookup table size (CSV)') +    return parser.parse_args() + +def main(): +    args = get_options() +    keys = ['opt', 'ports', 'dataw', 'mtu', 'rlutsize'] +    for opt in args.opt.strip().split(','): +        for ports in args.ports.strip().split(','): +            for dataw in args.dataw.strip().split(','): +                for mtu in args.mtu.strip().split(','): +                    for rlutsize in args.rlutsize.strip().split(','): +                        # Collect parameters +                        transform = {'opt':opt, 'ports':ports, 'dataw':dataw, 'mtu':mtu, 'rlutsize':rlutsize} +                        synth_run.synth_run(modname, keys, transform) + +if __name__ == '__main__': +    main() diff --git a/fpga/usrp3/lib/rfnoc/crossbar/synth/synth_run.py b/fpga/usrp3/lib/rfnoc/crossbar/synth/synth_run.py new file mode 100644 index 000000000..a9801ac20 --- /dev/null +++ b/fpga/usrp3/lib/rfnoc/crossbar/synth/synth_run.py @@ -0,0 +1,67 @@ +#! /usr/bin/python3 +#!/usr/bin/python3 +# +# Copyright 2018 Ettus Research, a National Instruments Company +# +# SPDX-License-Identifier: LGPL-3.0-or-later +# + +import sys, os +import subprocess +import re + +def synth_run(modname, keys, transform): +    prefix = modname + '_' + ('_'.join(['%s%s'%(k,transform[k]) for k in keys])) +    print('='*(len(prefix)+2)) +    print(' %s '%(prefix)) +    print('='*(len(prefix)+2)) +    # Write Verilog top-level file +    with open(modname + '_top.v.in', 'r') as in_file: +        with open(modname + '_top.v', 'w') as out_file: +            out_file.write(in_file.read().format(**transform)) +    # Run Vivado +    exitcode = subprocess.Popen( +        'vivado -mode tcl -source %s_top.tcl -nolog -nojou'%(modname), shell=True +    ).wait() +    if exitcode != 0: +        raise RuntimeError('Error running vivado. Was setupenv.sh run?') +    # Extract info +    lut = 100.0 +    reg = 100.0 +    bram = 100.0 +    dsp = 100.0 +    fmax = 0.0 +    with open(modname + '.rpt', 'r') as rpt_file: +        rpt = rpt_file.readlines() +    for line in rpt: +        lm = re.match(r'.*Slice LUTs\*.*\|(.*)\|(.*)\|(.*)\|(.*)\|.*', line) +        if lm is not None: +            lut = float(lm.group(1).strip()) +        rm = re.match(r'.*Slice Registers.*\|(.*)\|(.*)\|(.*)\|(.*)\|.*', line) +        if rm is not None: +            reg = float(rm.group(1).strip()) +        bm = re.match(r'.*Block RAM Tile.*\|(.*)\|(.*)\|(.*)\|(.*)\|.*', line) +        if bm is not None: +            bram = float(bm.group(1).strip()) +        dm = re.match(r'.*DSPs.*\|(.*)\|(.*)\|(.*)\|(.*)\|.*', line) +        if dm is not None: +            dsp = float(dm.group(1).strip()) +        tm = re.match(r'.*clk.*\| clk\s*\|(.*)\|.*\|.*\|.*\|.*\|.*\|.*\|.*\|', line) +        if tm is not None: +            fmax = 1000.0/float(tm.group(1).strip()) +    # Save report +    os.rename(modname + '.rpt', prefix + '.rpt') +    os.rename(modname + '.dcp', prefix + '.dcp') +    try: +        os.remove(modname + '_top.v') +        os.remove('fsm_encoding.os') +    except FileNotFoundError: +        pass +    # Write summary report line +    res_keys = ['lut','reg','bram','dsp','fmax'] +    res = {'lut':lut, 'reg':reg, 'bram':bram, 'dsp':dsp, 'fmax':fmax, 'prefix':prefix} +    if not os.path.exists(modname + '_summary.csv'): +        with open(modname + '_summary.csv', 'w') as summaryf: +            summaryf.write((','.join(keys + res_keys)) + '\n') +    with open(modname + '_summary.csv', 'a') as summaryf: +        summaryf.write((','.join(['%s'%(transform[k]) for k in keys])) + ',' + (','.join(['%.1f'%(res[k]) for k in res_keys])) + '\n') diff --git a/fpga/usrp3/lib/rfnoc/crossbar/torus_2d_dor_router_multi_sw.v b/fpga/usrp3/lib/rfnoc/crossbar/torus_2d_dor_router_multi_sw.v new file mode 100644 index 000000000..cd70450a0 --- /dev/null +++ b/fpga/usrp3/lib/rfnoc/crossbar/torus_2d_dor_router_multi_sw.v @@ -0,0 +1,338 @@ +// +// Copyright 2018 Ettus Research, A National Instruments Company +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// +// Module: torus_2d_dor_router_multi_sw +// Description: +//   Alternate implementation for torus_2d_dor_router_single_sw with +//   multiple switches for independent paths between inputs and outputs +//   **NOTE**: This module has not been validated + +module torus_2d_dor_router_multi_sw #( +  parameter                         WIDTH          = 64, +  parameter                         DIM_SIZE       = 4, +  parameter [$clog2(DIM_SIZE)-1:0]  XB_ADDR_X      = 0, +  parameter [$clog2(DIM_SIZE)-1:0]  XB_ADDR_Y      = 0, +  parameter                         TERM_BUFF_SIZE = 5, +  parameter                         XB_BUFF_SIZE   = 5, +  parameter                         ROUTING_ALLOC  = "WORMHOLE" +) ( +  // Clocks and resets +  input  wire             clk, +  input  wire             reset, + +  // Terminal connections +  input  wire [WIDTH-1:0] s_axis_term_tdata, +  input  wire             s_axis_term_tlast, +  input  wire             s_axis_term_tvalid, +  output wire             s_axis_term_tready, +  output wire [WIDTH-1:0] m_axis_term_tdata, +  output wire             m_axis_term_tlast, +  output wire             m_axis_term_tvalid, +  input  wire             m_axis_term_tready, + +  // X-dimension inter-XB connections +  input  wire [WIDTH-1:0] s_axis_xdim_tdata, +  input  wire [0:0]       s_axis_xdim_tdest, +  input  wire             s_axis_xdim_tlast, +  input  wire             s_axis_xdim_tvalid, +  output wire             s_axis_xdim_tready, +  output wire [WIDTH-1:0] m_axis_xdim_tdata, +  output wire [0:0]       m_axis_xdim_tdest, +  output wire             m_axis_xdim_tlast, +  output wire             m_axis_xdim_tvalid, +  input  wire             m_axis_xdim_tready, + +  // Y-dimension inter-XB connections +  input  wire [WIDTH-1:0] s_axis_ydim_tdata, +  input  wire [0:0]       s_axis_ydim_tdest, +  input  wire             s_axis_ydim_tlast, +  input  wire             s_axis_ydim_tvalid, +  output wire             s_axis_ydim_tready, +  output wire [WIDTH-1:0] m_axis_ydim_tdata, +  output wire [0:0]       m_axis_ydim_tdest, +  output wire             m_axis_ydim_tlast, +  output wire             m_axis_ydim_tvalid, +  input  wire             m_axis_ydim_tready +); + +  // ------------------------------------------------- +  // Routing functions +  // ------------------------------------------------- +  `include "mesh_node_mapping.vh" + +  function [2:0] term_in_route; +    input [WIDTH:0] header; +    reg [$clog2(DIM_SIZE)-1:0] xdst, ydst, xdiff, ydiff; +  begin +    xdst  = node_to_xdst(header); +    ydst  = node_to_ydst(header); +    xdiff = xdst - XB_ADDR_X; +    ydiff = ydst - XB_ADDR_Y; +    // Routing logic +    // - MSB is the VC, 2 LSBs are the router destination +    // - Long journeys get VC = 1 to bypass local traffic +    if (xdst == XB_ADDR_X && ydst == XB_ADDR_Y) begin +      term_in_route = {1'b0 /* VC don't care */, 2'd2 /* term out */}; +    end else if (xdst == XB_ADDR_X) begin +      term_in_route = {ydiff[$clog2(DIM_SIZE)-1], 2'd0 /* ydim out */}; +    end else begin +      term_in_route = {xdiff[$clog2(DIM_SIZE)-1], 2'd1 /* xdim out */}; +    end +  end +  endfunction + +  function [2:0] xdim_in_route; +    input [WIDTH:0] header; +    reg [$clog2(DIM_SIZE)-1:0] xdst, ydst, xdiff, ydiff; +  begin +    xdst  = node_to_xdst(header); +    ydst  = node_to_ydst(header); +    xdiff = xdst - XB_ADDR_X; +    ydiff = ydst - XB_ADDR_Y; +    // Routing logic +    // - MSB is the VC, 2 LSBs are the router destination +    // - Long journeys get VC = 1 to bypass local traffic +    if (xdst == XB_ADDR_X && ydst == XB_ADDR_Y) begin +      xdim_in_route = {1'b0 /* VC don't care */, 2'd2 /* term out */}; +    end else if (xdst == XB_ADDR_X) begin +      xdim_in_route = {ydiff[$clog2(DIM_SIZE)-1], 2'd0 /* ydim out */}; +    end else begin +      xdim_in_route = {xdiff[$clog2(DIM_SIZE)-1], 2'd1 /* xdim out */}; +    end +  end +  endfunction + +  function [1:0] ydim_in_route; +    input [WIDTH:0] header; +    reg [$clog2(DIM_SIZE)-1:0] ydst, ydiff; +  begin +    ydst  = node_to_ydst(header); +    ydiff = ydst - XB_ADDR_Y; +    // Routing logic +    // - MSB is the VC, LSB is the router destination +    // - Long journeys get VC = 1 to bypass local traffic +    if (ydst == XB_ADDR_Y) begin +      ydim_in_route = {1'b0 /* VC don't care */, 1'd1 /* term out */}; +    end else begin +      ydim_in_route = {ydiff[$clog2(DIM_SIZE)-1], 1'd0 /* ydim out */}; +    end +  end +  endfunction + +  // ------------------------------------------------- +  // Input demuxes +  // ------------------------------------------------- +  wire [WIDTH-1:0] ti_gt_tdata; +  wire             ti_gt_tdest;  +  wire             ti_gt_tlast;  +  wire             ti_gt_tvalid; +  wire             ti_gt_tready; +  wire [WIDTH-1:0] t2t_tdata,  t2x_tdata,  t2y_tdata; +  wire             t2t_tdest,  t2x_tdest,  t2y_tdest; +  wire             t2t_tlast,  t2x_tlast,  t2y_tlast; +  wire             t2t_tvalid, t2x_tvalid, t2y_tvalid; +  wire             t2t_tready, t2x_tready, t2y_tready; +  wire [WIDTH-1:0] term_in_hdr; +  wire [1:0]       term_in_port; + +  assign {ti_gt_tdest, term_in_port} = term_in_route(term_in_hdr); + +  axi_packet_gate #( +    .WIDTH(WIDTH), .SIZE(TERM_BUFF_SIZE) +  ) term_in_pkt_gate_i ( +    .clk      (clk),  +    .reset    (reset),  +    .clear    (1'b0), +    .i_tdata  (s_axis_term_tdata), +    .i_tlast  (s_axis_term_tlast), +    .i_tvalid (s_axis_term_tvalid), +    .i_tready (s_axis_term_tready), +    .i_terror (1'b0), +    .o_tdata  (ti_gt_tdata), +    .o_tlast  (ti_gt_tlast), +    .o_tvalid (ti_gt_tvalid), +    .o_tready (ti_gt_tready) +  ); + +  axi_demux #( +    .WIDTH(WIDTH+1), .SIZE(3), +    .PRE_FIFO_SIZE(0 /* must be 0 */), .POST_FIFO_SIZE(0) +  ) term_in_demux_i ( +    .clk      (clk), +    .reset    (reset), +    .clear    (1'b0), +    .header   (term_in_hdr), +    .dest     (term_in_port), +    .i_tdata  ({ti_gt_tdest, ti_gt_tdata}), +    .i_tlast  (ti_gt_tlast), +    .i_tvalid (ti_gt_tvalid), +    .i_tready (ti_gt_tready), +    .o_tdata  ({t2t_tdest, t2t_tdata, t2x_tdest, t2x_tdata, t2y_tdest, t2y_tdata}), +    .o_tlast  ({t2t_tlast,  t2x_tlast,  t2y_tlast}), +    .o_tvalid ({t2t_tvalid, t2x_tvalid, t2y_tvalid}), +    .o_tready ({t2t_tready, t2x_tready, t2y_tready}) +  ); + +  wire [WIDTH-1:0] xi_gt_tdata; +  wire             xi_gt_tdest;  +  wire             xi_gt_tlast;  +  wire             xi_gt_tvalid; +  wire             xi_gt_tready; +  wire [WIDTH-1:0] x2t_tdata,  x2x_tdata,  x2y_tdata; +  wire             x2t_tdest,  x2x_tdest,  x2y_tdest; +  wire             x2t_tlast,  x2x_tlast,  x2y_tlast; +  wire             x2t_tvalid, x2x_tvalid, x2y_tvalid; +  wire             x2t_tready, x2x_tready, x2y_tready; +  wire [WIDTH-1:0] xdim_in_hdr; +  wire [1:0]       xdim_in_port; + +  assign {xi_gt_tdest, xdim_in_port} = xdim_in_route(xdim_in_hdr); + +  axis_ingress_vc_buff #( +    .WIDTH(WIDTH), .NUM_VCS(2), +    .SIZE(XB_BUFF_SIZE), +    .ROUTING(ROUTING_ALLOC) +  ) xdim_in_vc_buf_i ( +    .clk           (clk),  +    .reset         (reset),  +    .s_axis_tdata  (s_axis_xdim_tdata), +    .s_axis_tdest  (s_axis_xdim_tdest), +    .s_axis_tlast  (s_axis_xdim_tlast), +    .s_axis_tvalid (s_axis_xdim_tvalid), +    .s_axis_tready (s_axis_xdim_tready), +    .m_axis_tdata  (xi_gt_tdata), +    .m_axis_tlast  (xi_gt_tlast), +    .m_axis_tvalid (xi_gt_tvalid), +    .m_axis_tready (xi_gt_tready) +  ); + +  axi_demux #( +    .WIDTH(WIDTH+1), .SIZE(3), +    .PRE_FIFO_SIZE(0 /* must be 0 */), .POST_FIFO_SIZE(0) +  ) xdim_in_demux_i ( +    .clk      (clk), +    .reset    (reset), +    .clear    (1'b0), +    .header   (xdim_in_hdr), +    .dest     (xdim_in_port), +    .i_tdata  ({xi_gt_tdest, xi_gt_tdata}), +    .i_tlast  (xi_gt_tlast), +    .i_tvalid (xi_gt_tvalid), +    .i_tready (xi_gt_tready), +    .o_tdata  ({x2t_tdest, x2t_tdata, x2x_tdest, x2x_tdata, x2y_tdest, x2y_tdata}), +    .o_tlast  ({x2t_tlast, x2x_tlast, x2y_tlast}), +    .o_tvalid ({x2t_tvalid, x2x_tvalid, x2y_tvalid}), +    .o_tready ({x2t_tready, x2x_tready, x2y_tready}) +  ); + +  wire [WIDTH-1:0] yi_gt_tdata; +  wire             yi_gt_tdest;  +  wire             yi_gt_tlast;  +  wire             yi_gt_tvalid; +  wire             yi_gt_tready; +  wire [WIDTH-1:0] y2t_tdata,  y2y_tdata; +  wire             y2t_tdest,  y2y_tdest; +  wire             y2t_tlast,  y2y_tlast; +  wire             y2t_tvalid, y2y_tvalid; +  wire             y2t_tready, y2y_tready; +  wire [WIDTH-1:0] ydim_in_hdr; +  wire [0:0]       ydim_in_port; + +  assign {yi_gt_tdest, ydim_in_port} = ydim_in_route(ydim_in_hdr); + +  axis_ingress_vc_buff #( +    .WIDTH(WIDTH), .NUM_VCS(2), +    .SIZE(XB_BUFF_SIZE), +    .ROUTING(ROUTING_ALLOC) +  ) ydim_in_vc_buf_i ( +    .clk           (clk),  +    .reset         (reset),  +    .s_axis_tdata  (s_axis_ydim_tdata ), +    .s_axis_tdest  (s_axis_ydim_tdest ), +    .s_axis_tlast  (s_axis_ydim_tlast ), +    .s_axis_tvalid (s_axis_ydim_tvalid), +    .s_axis_tready (s_axis_ydim_tready), +    .m_axis_tdata  (yi_gt_tdata ), +    .m_axis_tlast  (yi_gt_tlast ), +    .m_axis_tvalid (yi_gt_tvalid), +    .m_axis_tready (yi_gt_tready) +  ); + +  axi_demux #( +    .WIDTH(WIDTH+1), .SIZE(2), +    .PRE_FIFO_SIZE(0 /* must be 0 */), .POST_FIFO_SIZE(0) +  ) ydim_in_demux_i ( +    .clk      (clk), +    .reset    (reset), +    .clear    (1'b0), +    .header   (ydim_in_hdr), +    .dest     (ydim_in_port), +    .i_tdata  ({yi_gt_tdest, yi_gt_tdata}), +    .i_tlast  (yi_gt_tlast), +    .i_tvalid (yi_gt_tvalid), +    .i_tready (yi_gt_tready), +    .o_tdata  ({y2t_tdest, y2t_tdata, y2y_tdest, y2y_tdata}), +    .o_tlast  ({y2t_tlast,  y2y_tlast}), +    .o_tvalid ({y2t_tvalid, y2y_tvalid}), +    .o_tready ({y2t_tready, y2y_tready}) +  ); + +  // ------------------------------------------------- +  // Output muxes +  // ------------------------------------------------- +  wire term_tdest_discard; +  axi_mux #( +    .WIDTH(WIDTH+1), .SIZE(3), +    .PRE_FIFO_SIZE(0), .POST_FIFO_SIZE(1) +  ) term_out_mux_i ( +    .clk      (clk), +    .reset    (reset), +    .clear    (1'b0), +    .i_tdata  ({t2t_tdest, t2t_tdata, x2t_tdest, x2t_tdata, y2t_tdest, y2t_tdata}), +    .i_tlast  ({t2t_tlast, x2t_tlast, y2t_tlast }), +    .i_tvalid ({t2t_tvalid, x2t_tvalid, y2t_tvalid}), +    .i_tready ({t2t_tready, x2t_tready, y2t_tready}), +    .o_tdata  ({term_tdest_discard, m_axis_term_tdata}), +    .o_tlast  (m_axis_term_tlast), +    .o_tvalid (m_axis_term_tvalid), +    .o_tready (m_axis_term_tready) +  ); + +  axi_mux #( +    .WIDTH(WIDTH+1), .SIZE(2), +    .PRE_FIFO_SIZE(0), .POST_FIFO_SIZE(1) +  ) xdim_out_mux_i ( +    .clk      (clk), +    .reset    (reset), +    .clear    (1'b0), +    .i_tdata  ({t2x_tdest, t2x_tdata, x2x_tdest, x2x_tdata}), +    .i_tlast  ({t2x_tlast,  x2x_tlast}), +    .i_tvalid ({t2x_tvalid, x2x_tvalid}), +    .i_tready ({t2x_tready, x2x_tready}), +    .o_tdata  ({m_axis_xdim_tdest, m_axis_xdim_tdata}), +    .o_tlast  (m_axis_xdim_tlast ), +    .o_tvalid (m_axis_xdim_tvalid), +    .o_tready (m_axis_xdim_tready) +  ); + +  axi_mux #( +    .WIDTH(WIDTH+1), .SIZE(3), +    .PRE_FIFO_SIZE(0), .POST_FIFO_SIZE(1) +  ) ydim_out_mux_i ( +    .clk      (clk), +    .reset    (reset), +    .clear    (1'b0), +    .i_tdata  ({t2y_tdest, t2y_tdata, x2y_tdest, x2y_tdata, y2y_tdest, y2y_tdata}), +    .i_tlast  ({t2y_tlast, x2y_tlast, y2y_tlast }), +    .i_tvalid ({t2y_tvalid, x2y_tvalid, y2y_tvalid}), +    .i_tready ({t2y_tready, x2y_tready, y2y_tready}), +    .o_tdata  ({m_axis_ydim_tdest, m_axis_ydim_tdata}), +    .o_tlast  (m_axis_ydim_tlast), +    .o_tvalid (m_axis_ydim_tvalid), +    .o_tready (m_axis_ydim_tready) +  ); + +endmodule + diff --git a/fpga/usrp3/lib/rfnoc/crossbar/torus_2d_dor_router_single_sw.v b/fpga/usrp3/lib/rfnoc/crossbar/torus_2d_dor_router_single_sw.v new file mode 100644 index 000000000..21a66782d --- /dev/null +++ b/fpga/usrp3/lib/rfnoc/crossbar/torus_2d_dor_router_single_sw.v @@ -0,0 +1,294 @@ +// +// Copyright 2018 Ettus Research, A National Instruments Company +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// +// Module: torus_2d_dor_router_single_sw +// Description:  +//   This module implements the router for a 2-dimentional (2d)  +//   torus network that uses dimension order routing (dor) and has a  +//   single underlying switch (single_sw). It uses AXI-Stream for all of its +//   links.  +//   The torus topology, routing algorithms and the router architecture is  +//   described in README.md in this directory.  +// Parameters: +//   - WIDTH: Width of the AXI-Stream data bus +//   - DIM_SIZE: Number of routers alone one dimension +//   - XB_ADDR_X: The X-coordinate of this router in the topology +//   - XB_ADDR_Y: The Y-coordinate of this router in the topology +//   - TERM_BUFF_SIZE: log2 of the ingress terminal buffer size (in words) +//   - XB_BUFF_SIZE: log2 of the ingress inter-router buffer size (in words) +//   - ROUTING_ALLOC: Algorithm to allocate routing paths between routers. +//     * WORMHOLE: Allocate route as soon as first word in pkt arrives +//     * CUT-THROUGH: Allocate route only after the full pkt arrives +//   - SWITCH_ALLOC: Algorithm to allocate the switch +//     * PRIO: Priority based. Priority: Y-dim > X-dim > Term +//     * ROUND-ROBIN: Round robin input port allocation +// Signals: +//   - *_axis_term_*: Terminal ports (master/slave) +//   - *_axis_xdim_*: Inter-router X-dim connections (master/slave) +//   - *_axis_ydim_*: Inter-router Y-dim connections (master/slave) +// + +module torus_2d_dor_router_single_sw #( +  parameter                         WIDTH          = 64, +  parameter                         DIM_SIZE       = 4, +  parameter [$clog2(DIM_SIZE)-1:0]  XB_ADDR_X      = 0, +  parameter [$clog2(DIM_SIZE)-1:0]  XB_ADDR_Y      = 0, +  parameter                         TERM_BUFF_SIZE = 5, +  parameter                         XB_BUFF_SIZE   = 5, +  parameter                         ROUTING_ALLOC  = "WORMHOLE", +  parameter                         SWITCH_ALLOC   = "PRIO" +) ( +  // Clocks and resets +  input  wire             clk, +  input  wire             reset, + +  // Terminal connections +  input  wire [WIDTH-1:0] s_axis_term_tdata, +  input  wire             s_axis_term_tlast, +  input  wire             s_axis_term_tvalid, +  output wire             s_axis_term_tready, +  output wire [WIDTH-1:0] m_axis_term_tdata, +  output wire             m_axis_term_tlast, +  output wire             m_axis_term_tvalid, +  input  wire             m_axis_term_tready, + +  // X-dimension inter-XB connections +  input  wire [WIDTH-1:0] s_axis_xdim_tdata, +  input  wire [0:0]       s_axis_xdim_tdest, +  input  wire             s_axis_xdim_tlast, +  input  wire             s_axis_xdim_tvalid, +  output wire             s_axis_xdim_tready, +  output wire [WIDTH-1:0] m_axis_xdim_tdata, +  output wire [0:0]       m_axis_xdim_tdest, +  output wire             m_axis_xdim_tlast, +  output wire             m_axis_xdim_tvalid, +  input  wire             m_axis_xdim_tready, + +  // Y-dimension inter-XB connections +  input  wire [WIDTH-1:0] s_axis_ydim_tdata, +  input  wire [0:0]       s_axis_ydim_tdest, +  input  wire             s_axis_ydim_tlast, +  input  wire             s_axis_ydim_tvalid, +  output wire             s_axis_ydim_tready, +  output wire [WIDTH-1:0] m_axis_ydim_tdata, +  output wire [0:0]       m_axis_ydim_tdest, +  output wire             m_axis_ydim_tlast, +  output wire             m_axis_ydim_tvalid, +  input  wire             m_axis_ydim_tready +); + +  //------------------------------------------------- +  // Routing and switch allocation functions +  //------------------------------------------------- + +  // mesh_node_mapping.vh file contains the mapping between the node number +  // and its XY coordinates. It is autogenerated and defines the node_to_xdst +  // and node_to_ydst functions. +  `include "mesh_node_mapping.vh" + +  localparam [1:0] SW_DEST_TERM = 2'd0; +  localparam [1:0] SW_DEST_XDIM = 2'd1; +  localparam [1:0] SW_DEST_YDIM = 2'd2; +  localparam [1:0] SW_NUM_DESTS = 2'd3; + +  // The compute_switch_tdest function is the destination selector +  // i.e. it will inspecte the bottom $clog2(DIM_SIZE)*2 bits of the +  // first word of a packet and determine the destination of the packet.  +  function [2:0] compute_switch_tdest; +    input [WIDTH-1:0] header; +    reg [$clog2(DIM_SIZE)-1:0] xdst, ydst; +    reg signed [$clog2(DIM_SIZE):0] xdiff, ydiff; +  begin +    xdst  = node_to_xdst(header); +    ydst  = node_to_ydst(header); +    xdiff = xdst - XB_ADDR_X; +    ydiff = ydst - XB_ADDR_Y; +    // Routing logic +    // - MSB is the VC, 2 LSBs are the router destination +    // - Long journeys get VC = 1 to bypass local traffic +    if (xdiff == 'd0 && ydiff == 'd0) begin +      compute_switch_tdest = {1'b0 /* VC don't care */,  SW_DEST_TERM}; +    end else if (xdiff != 'd0) begin +      compute_switch_tdest = {(xdiff < 0), SW_DEST_XDIM}; +    end else begin +      compute_switch_tdest = {(ydiff < 0), SW_DEST_YDIM}; +    end +    //$display("xdst=%d, ydst=%d, xaddr=%d, yaddr=%d, dst=%d", xdst, ydst, XB_ADDR_X, XB_ADDR_Y, compute_switch_tdest); +  end +  endfunction + +  // The compute_switch_alloc function is the switch allocation function  +  // i.e. it chooses which input port reserves the switch for packet transfer. +  // After the switch is allocated, all other ports will be backpressured until +  // the packet finishes transferring. +  function [1:0] compute_switch_alloc; +    input [2:0] pkt_waiting; +    input [1:0] last_alloc; +  begin +    if (pkt_waiting == 3'b000) begin +      compute_switch_alloc = SW_DEST_TERM; +    end else if (pkt_waiting == 3'b001) begin +      compute_switch_alloc = SW_DEST_TERM; +    end else if (pkt_waiting == 3'b010) begin +      compute_switch_alloc = SW_DEST_XDIM; +    end else if (pkt_waiting == 3'b100) begin +      compute_switch_alloc = SW_DEST_YDIM; +    end else begin +      if (SWITCH_ALLOC == "PRIO") begin +        // Priority: Y-dim > X-dim > Term +        if (pkt_waiting[SW_DEST_YDIM]) +          compute_switch_alloc = SW_DEST_YDIM; +        else if (pkt_waiting[SW_DEST_XDIM]) +          compute_switch_alloc = SW_DEST_XDIM; +        else +          compute_switch_alloc = SW_DEST_TERM; +      end else begin +        // Round-robin +        if (pkt_waiting[(last_alloc + 3'd1) % SW_NUM_DESTS]) +          compute_switch_alloc = (last_alloc + 3'd1) % SW_NUM_DESTS; +        else if (pkt_waiting[(last_alloc + 3'd2) % SW_NUM_DESTS]) +          compute_switch_alloc = (last_alloc + 3'd2) % SW_NUM_DESTS; +        else +          compute_switch_alloc = last_alloc; +      end +    end +  end +  endfunction + +  //------------------------------------------------- +  // Ingress buffers +  //------------------------------------------------- +  wire [WIDTH-1:0] ydim_in_data , xdim_in_data , term_in_data ; +  wire [2:0]       ydim_in_dest , xdim_in_dest , term_in_dest ;  +  wire             ydim_in_last , xdim_in_last , term_in_last ;  +  wire             ydim_in_valid, xdim_in_valid, term_in_valid; +  wire             ydim_in_ready, xdim_in_ready, term_in_ready; + +  // Data coming in from the terminal is gated until a full packet arrives +  // in order to minimize the switch allocation time per packet. +  axi_packet_gate #( +    .WIDTH(WIDTH), .SIZE(TERM_BUFF_SIZE) +  ) term_in_pkt_gate_i ( +    .clk      (clk),  +    .reset    (reset),  +    .clear    (1'b0), +    .i_tdata  (s_axis_term_tdata), +    .i_tlast  (s_axis_term_tlast), +    .i_tvalid (s_axis_term_tvalid), +    .i_tready (s_axis_term_tready), +    .i_terror (1'b0), +    .o_tdata  (term_in_data), +    .o_tlast  (term_in_last), +    .o_tvalid (term_in_valid), +    .o_tready (term_in_ready) +  ); +  assign term_in_dest = compute_switch_tdest(term_in_data); + +  // The XY directions have buffers with 2 virtual channels to minimize the +  // possibility of a deadlock. +  axis_ingress_vc_buff #( +    .WIDTH(WIDTH), .NUM_VCS(2), +    .SIZE(XB_BUFF_SIZE), +    .ROUTING(ROUTING_ALLOC) +  ) xdim_in_vc_buf_i ( +    .clk           (clk),  +    .reset         (reset),  +    .s_axis_tdata  (s_axis_xdim_tdata), +    .s_axis_tdest  (s_axis_xdim_tdest), +    .s_axis_tlast  (s_axis_xdim_tlast), +    .s_axis_tvalid (s_axis_xdim_tvalid), +    .s_axis_tready (s_axis_xdim_tready), +    .m_axis_tdata  (xdim_in_data), +    .m_axis_tlast  (xdim_in_last), +    .m_axis_tvalid (xdim_in_valid), +    .m_axis_tready (xdim_in_ready) +  ); +  assign xdim_in_dest = compute_switch_tdest(xdim_in_data); + +  axis_ingress_vc_buff #( +    .WIDTH(WIDTH), .NUM_VCS(2), +    .SIZE(XB_BUFF_SIZE), +    .ROUTING(ROUTING_ALLOC) +  ) ydim_in_vc_buf_i ( +    .clk           (clk),  +    .reset         (reset),  +    .s_axis_tdata  (s_axis_ydim_tdata ), +    .s_axis_tdest  (s_axis_ydim_tdest ), +    .s_axis_tlast  (s_axis_ydim_tlast ), +    .s_axis_tvalid (s_axis_ydim_tvalid), +    .s_axis_tready (s_axis_ydim_tready), +    .m_axis_tdata  (ydim_in_data ), +    .m_axis_tlast  (ydim_in_last ), +    .m_axis_tvalid (ydim_in_valid), +    .m_axis_tready (ydim_in_ready) +  ); +  assign ydim_in_dest = compute_switch_tdest(ydim_in_data); + +  //------------------------------------------------- +  // Switch +  //------------------------------------------------- + +  // Track the input packet state +  localparam [0:0] PKT_ST_HEAD = 1'b0; +  localparam [0:0] PKT_ST_BODY = 1'b1; +  reg [0:0] pkt_state = PKT_ST_HEAD; + +  // The switch only accept packets on a single port at a time. +  wire sw_in_ready = |({ydim_in_ready, xdim_in_ready, term_in_ready}); +  wire sw_in_valid = |({ydim_in_valid, xdim_in_valid, term_in_valid}); +  wire sw_in_last  = |({ydim_in_last&ydim_in_valid, xdim_in_last&xdim_in_valid, term_in_last&term_in_valid}); + +  always @(posedge clk) begin +    if (reset) begin +      pkt_state <= PKT_ST_HEAD; +    end else if (sw_in_valid & sw_in_ready) begin +      pkt_state <= sw_in_last ? PKT_ST_HEAD : PKT_ST_BODY; +    end +  end + +  // The switch requires the allocation to stay valid until the +  // end of the packet. We also might need to keep the previous +  // packet's allocation to compute the current one +  wire [1:0] switch_alloc; +  reg  [1:0] prev_switch_alloc = SW_DEST_TERM; +  reg  [1:0] pkt_switch_alloc  = SW_DEST_TERM; + +  always @(posedge clk) begin +    if (reset) begin +      prev_switch_alloc <= SW_DEST_TERM; +      pkt_switch_alloc <= SW_DEST_TERM; +    end else if (sw_in_valid & sw_in_ready) begin +      if (pkt_state == PKT_ST_HEAD) +        pkt_switch_alloc <= switch_alloc; +      if (sw_in_last) +        prev_switch_alloc <= switch_alloc; +    end +  end + +  assign switch_alloc = (sw_in_valid && pkt_state == PKT_ST_HEAD) ?  +    compute_switch_alloc({ydim_in_valid, xdim_in_valid, term_in_valid}, prev_switch_alloc) : +    pkt_switch_alloc; + +  wire term_tdest_discard; +  axis_switch #( +    .DATA_W(WIDTH), .DEST_W(1), .IN_PORTS(3), .OUT_PORTS(3) +  ) switch_i ( +    .clk           (clk), +    .reset         (reset), +    .s_axis_tdata  ({ydim_in_data , xdim_in_data , term_in_data }), +    .s_axis_tdest  ({ydim_in_dest , xdim_in_dest , term_in_dest }),  +    .s_axis_tlast  ({ydim_in_last , xdim_in_last , term_in_last }), +    .s_axis_tvalid ({ydim_in_valid, xdim_in_valid, term_in_valid}), +    .s_axis_tready ({ydim_in_ready, xdim_in_ready, term_in_ready}), +    .s_axis_alloc  (switch_alloc), +    .m_axis_tdata  ({m_axis_ydim_tdata,  m_axis_xdim_tdata,  m_axis_term_tdata }), +    .m_axis_tdest  ({m_axis_ydim_tdest,  m_axis_xdim_tdest,  term_tdest_discard}), +    .m_axis_tlast  ({m_axis_ydim_tlast,  m_axis_xdim_tlast,  m_axis_term_tlast }), +    .m_axis_tvalid ({m_axis_ydim_tvalid, m_axis_xdim_tvalid, m_axis_term_tvalid}), +    .m_axis_tready ({m_axis_ydim_tready, m_axis_xdim_tready, m_axis_term_tready}) +  ); + +endmodule +  | 
