Home CONNECT AXI Wrapper Documentation (Draft)
Post
Cancel

CONNECT AXI Wrapper Documentation (Draft)

Introduction

The AXI4[-Stream] wrapper is built over the CONNECT NoC, which is a network generator targeting FPGA.

Getting Started

First, clone the CONNECT repository and build a sample network.

TODO: Replace the git repository address in the future.

1
2
3
$ git clone https://github.com/shili2017/connect.git
$ cd connect
$ python gen_network.py -t double_ring -w 38 -n 4 -v 2 -d 4 --gen_rtl --gen_graph

The network will be generated and put in the build directory. Then we can run the sample test bench.

1
2
3
$ cp Prims/run_modelsim.sh build/
$ cd build
$ ./run_modelsim.sh

The default sample test bench is for AXI4-Stream protocol. To test on AXI4 standard protocol, in run_modelsim.sh, replace CONNECT_testbench_sample_axi4stream by CONNECT_testbench_sample_axi4.

To change (a) flit width or (b) number of virtual channels or (c) flit buffer depth, first re-generate the network.

1
$ python gen_network.py -t double_ring -w <flit_width> -n 4 -v <num_vcs> -d <flit_buffer_depth> --gen_rtl --gen_graph

Then change the parameters in connect_parameters.v.

1
2
3
`define NUM_VCS <num_vcs>
`define FLIT_DATA_WIDTH <flit_depth>
`define FLIT_BUFFER_DEPTH <flit_buffer_depth>

Do not change number of send or receive ports for now. So far the wrapper is still written manually, and only supports 4 terminals in the network.

TODO: Consider generating the network wrapper automatically with scripts.

To prevent potential deadlocks, AXI4 standard protocol requires at least 2 virtual channels, one for requests and the other for responses, and responses have higher priority. Meanwhile, AXI4-Stream only needs 1 virtual channel.

Overall Design

The overall design is shown in the following blocks.

AXI4:

1
2
3
4
5
6
7
8
9
10
                                        CLK_FPGA                                     |                      CLK_NOC
---------------------------------------------------------------------------------------------------------------------

+--------+            +------------+             +--------------+             +-------------+             +---------+
| Device | <--AXI4--> | AXI4Bridge | ---flits--> |  Serializer  | ---flits--> | InPortFIFO  | ---flits--> |         |
+--------+            +------------+   97 bits   +--------------+    n bits   +-------------+    n bits   |         |
                            ^                                                                             | Network |
                            |                    +--------------+             +-------------+             |         |
                            +-----------flits--- | Deserializer | <--flits--- | OutPortFIFO | <--flits--- |         |
                                       97 bits   +--------------+    n bits   +-------------+    n bits   +---------+

AXI4-Stream:

1
2
3
4
5
6
7
8
9
10
                                                     CLK_FPGA                                      |                      CLK_NOC
-----------------------------------------------------------------------------------------------------------------------------------

+---------------+                   +------------+             +--------------+             +-------------+             +---------+
| Master Device | ---AXI4-Stream--> | AXI4Bridge | ---flits--> |  Serializer  | ---flits--> | InPortFIFO  | ---flits--> |         |
+---------------+                   +------------+   106 bits  +--------------+    n bits   +-------------+    n bits   |         |
                                                                                                                        | Network |
+---------------+                   +------------+             +--------------+             +-------------+             |         |
| Slave  Device | <--AXI4-Stream--- | AXI4Bridge | <--flits--- | Deserializer | <--flits--- | OutPortFIFO | <--flits--- |         |
+---------------+                   +------------+   106 bits  +--------------+    n bits   +-------------+    n bits   +---------+
  • NetworkAXI4Wrapper.sv

  • NetworkAXI4StreamWrapper.sv

AXI4[-Stream] Bridge Design

An AXI4 bridge is used to convert a pair of send & recv port to a standard AXI4 protocol interface, either as a master device or a slave device. For example, for a network with 4 endpoints, we can have 2 AXI4 master devices and 2 AXI4 slave devices.

An AXI4-Stream bridge is used to convert a send or recv port to a AXI4-Stream protocol master or slave interface. For example, for a network with 4 endpoints, we can have 4 AXI4-Stream master devices and 4 AXI4-Stream slave devices.

Parameters are defined in AXI4Package.sv.

1
2
3
4
5
6
7
parameter ADDR_WIDTH   = 32;
parameter DATA_WIDTH   = 64;
parameter STRB_WIDTH   = DATA_WIDTH / 8;
parameter ID_WIDTH     = 8;
parameter USER_WIDTH   = 8;
parameter KEEP_WIDTH   = DATA_WIDTH / 8;
parameter DEST_WIDTH   = 4;

Users also need to calculate the standard AXI4[-Stream] flit data width by themselves. For example, with the default parameters, we have

1
2
parameter AXI4_FLIT_DATA_WIDTH  = 76;
parameter AXI4S_FLIT_DATA_WIDTH = 93;

AXI4

AXI4 requests and responses are encoded in a single AXI4 flit. A standard AXI4 request or response with the aforementioned parameters is encoded into a 92-bit flit. The 3 most significant bits represent for the channel flag for the 5 AXI4 channels.

User devices are required to mark the source ID in AXI4 ID field, and AXI4 ID field is put in the LSB part in the flit to be identified by the serializer & deserializer.

AXI4 requests are sent in virtual channel 1, and AXI4 responses are sent in virtual channel 0, which has a higher priority to prevent potential deadlocks.

AW/AR Channel

1
2
3
4
5
6
7
8
9
10
11
12
AXI_x     - [75     ]
axid      - [74 : 67]
------ padding ------
axregion  - [60 : 57]
axqos     - [56 : 53]
axprot    - [52 : 50]
axcache   - [49 : 46]
axlock    - [45     ]
axburst   - [44 : 43]
axsize    - [42 : 40]
axlen     - [39 : 32]
axaddr    - [31 :  0]

W Channel

1
2
3
4
5
AXI_W     - [75     ]
------ padding ------
wlast     - [72     ]
wstrb     - [71 : 64]
wdata     - [63 :  0]

B Channel

1
2
3
4
5
AXI_W     - [75     ]
bid       - [74 : 67]
------ padding ------
bresp     - [65 : 64]
------ padding ------

R Channel

1
2
3
4
5
AXI_R     - [75     ]
rid       - [74 : 67]
rlast     - [66     ]
rresp     - [65 : 64]
rdata     - [63 :  0]

AXI4-Stream

AXI4-Stream requests are encoded in a single AXI4-Stream flit. A AXI4-Stream request with the aforementioned parameters is encoded into a 101-bit flit.

User devices are required to mark the source ID in AXI4-Stream ID field, and AXI4-Stream ID field is put in the LSB part in the flit to be identified by the serializer & deserializer.

T Channel

1
2
3
4
5
6
tdest - [92 : 89]
tid   - [88 : 81]
tlast - [80     ]
tkeep - [79 : 72]
tstrb - [71 : 64]
tdata - [63 :  0]
  • AXI4Bridge.sv

  • AXI4StreamBridge.sv

  • AXI4Interface.sv

  • AXI4Package.sv

Serializer & Deserializer Design

To handle mismatched sizes of AXI4[-Stream] protocol flits and actual flits to be sent, a serializer & deserializer for each master & slave device is needed. For example, for a AXI4-Stream request flit of 101+5 bits (5 is metadata, including valid bit, tail bit, etc.) and actual flit width of 38+5 bits, a single request needs to be split into 3 small flits by the serializer on the sender side and then small flits can be reassembled to a complete AXI request on the receiver side.

What makes things more complicated is that in the case that two master devices send data to a single slave device, flits may interleave (when virtual link is not enabled in CONNECT, or in general NoCs). Thus we need to encode source ID into the small flit such that the deserializer can distinguish interleaving flits. Fortunately, both AXI4 and AXI4-Stream supports interleaving requests and responses on the protocol level, so we don’t need to store the whole packet, whose size is unknown at the beginnning. Namely, for every single transfer in AXI4[-Stream], we set the tail bit to 1.

For the serializer, the number of flits is ceil(IN_FLIT_DATA_WIDTH / OUT_FLIT_EFF_DATA_WIDTH), where OUT_FLIT_EFF_DATA_WIDTH = OUT_FLIT_DATA_WIDTH - SRC_BITS. Similarly, for the deserializer, the number of flits is ceil(OUT_FLIT_DATA_WIDTH / IN_FLIT_EFF_DATA_WIDTH), where IN_FLIT_EFF_DATA_WIDTH = IN_FLIT_DATA_WIDTH - SRC_BITS. The structure of small flit is shown as follows,

1
2
3
4
 -----------------------------------------------------------------------------------
 | <valid bit> | <is_tail> | <destination> | <virtual channel> | <source> | <data> |
 -----------------------------------------------------------------------------------
        1            1        `DEST_BITS         `VC_BITS       `SRC_BITS  OUT_FLIT_EFF_DATA_WIDTH

To handle interleaving flits, currently we adopt only a simple solution, as we know that flits are in-order and we know the number of senders. We make a table with one row per source ID and just fill in the table with successive flits until the tail flit arrives. At that moment the flits can be reassembled into a AXI4 flit and be processed by a later stage. We also implement a priority encoder as an arbiter to handle multiple valid reassembled flits in a single cycle. See the code below.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// Use a priority encoder to decide which flit to send
logic [`SRC_BITS - 1 : 0] out_idx;
always_comb begin
  out_idx = 0;
  for (int i = `NUM_USER_SEND_PORTS - 1; i >= 0; i--) begin
    if (out_valid[i])
      out_idx = i;
  end
end
always_comb begin
  for (int i = 0; i < `NUM_USER_SEND_PORTS; i++) begin
    out_ready[i] = 0;
    out_flit = 0;
  end
  out_ready[out_idx] = out_flit_ready;
  out_flit = {flit_meta_reg[out_idx], flit_data_reg[out_idx][OUT_FLIT_DATA_WIDTH - 1 : 0]};
end
  • FlitSerializer.sv

Flow Control Layer Design

Credit-based flow control is supported in CONNECT and the wrapper. To support flow control, a synchronous FIFO, i.e., SCFIFO, is implemented as a buffer between the user devices (or more accurately, the serailizer or deserializer) and the network. On the sender side, we need to maintain a credit counter. Each time we send a flit, we need to decrement the counter, and each time we receive a credit from the network, we increment the counter. On the receiver side, each time we dequeue a flit from the FIFO to the user device, we send a credit to the network.

The network itself can run at a higher clock frequency as ASIC, and the remaining FPGA logic may run at a lower clock frequency. Thus a FIFO with different read and write clock, i.e., DCFIFO, is also needed. The block diagram of a FIFO is shown below.

InPortFIFO:

1
2
3
4
5
6
7
8
            (WR) CLK_FPGA   CLK_NOC (RD)             CLK_NOC
                        |   |                        |
                        v   v                        v                             +---------------+
                     +--------+                    +--------+                      |               | ---send_ports_putFlit_flit_in-->
---put_flit--------> |        | ---sc_enq_data---> |        | ---deq_flit_data---> | Combinational | ---EN_send_ports_putFlit------->
---put_flit_valid--> | DCFIFO | ---sc_enq_valid--> | SCFIFO | ---deq_flit_valid--> | Logic         | <--send_ports_getCredits--------
<--put_flit_ready--- |        | <--sc_enq_ready--- |        | <--deq_flit_ready--- |               | ---EN_send_ports_getCredits---->
                     +--------+                    +--------+                      +---------------+

OutPortFIFO:

1
2
3
4
5
6
7
8
            (RD) CLK_FPGA   CLK_NOC (WR)             CLK_NOC
                        |   |                        |
                        v   v                        v                             +---------------+
                     +--------+                    +--------+                      |               | <--recv_ports_getFlit------------
<--get_flit--------- |        | <--sc_deq_data---- |        | <--enq_flit_data---- | Combinational | ---EN_recv_ports_getFlit-------->
<--get_flit_valid--- | DCFIFO | <--sc_deq_valid--- | SCFIFO | <--enq_flit_valid--- | Logic         | ---recv_ports_putCredits_cr_in-->
---get_flit_ready--> |        | ---sc_deq_ready--> |        | ---enq_flit_ready--> |               | ---EN_recv_ports_putCredits----->
                     +--------+                    +--------+                      +---------------+

TODO: 1) Call AXI4 flit a packet. 2) One FIFO for each VC. 3) Add one more VC for W channel. 4) Do read and write in the same cycle? 5) #rows in deserializer should be #src * #vc. 6) One DCFIFO, handle credits approximately. 7) Serializer & deserializer should be close to NoC.

Parameters of both DCFIFO and SCFIFO can be modified by users.

SCFIFO:

1
2
3
4
parameter DEPTH               = `FLIT_BUFFER_DEPTH;
parameter DATA_WIDTH          = `FLIT_WIDTH;
parameter ALMOST_FULL_VALUE   = `FLIT_BUFFER_DEPTH - 1;
parameter ALMOST_EMPTY_VALUE  = 1;

DCFIFO:

1
2
parameter DEPTH               = `FLIT_BUFFER_DEPTH;
parameter DATA_WIDTH          = `FLIT_WIDTH;
  • FlitFIFO.sv

Testbench

To test the AXI4[-Stream] wrapper, we also need AXI4[-Stream] devices to send requests or receive responses. We also include testbenches to verify the correctness of AXI4[-Stream] wrapper design. See related source files.

  • AXI4Device.sv

  • AXI4StreamDevice.sv

  • testbench_sample_axi4.sv

  • testbench_sample_axi4stream.sv

This post is licensed under CC BY 4.0 by the author.