MatchLib
AxiSplitter.h
1 /*
2  * Copyright (c) 2018-2019, NVIDIA CORPORATION. All rights reserved.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License")
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 #ifndef __AXI_SPLITTER_H__
17 #define __AXI_SPLITTER_H__
18 
19 #include <systemc.h>
20 #include <nvhls_connections.h>
21 #include <nvhls_serdes.h>
22 #include <nvhls_packet.h>
23 #include <nvhls_int.h>
24 #include <nvhls_array.h>
25 #include <axi/axi4.h>
26 #include "Arbiter.h"
27 #include "TypeToBits.h"
28 
61 template <typename axiCfg, int numSlaves, int numAddrBitsToInspect = axiCfg::addrWidth, bool default_output = false, bool translate_addr = false>
62 class AxiSplitter : public sc_module {
63  public:
64  static const int kDebugLevel = 5;
65  sc_in<bool> clk;
66  sc_in<bool> reset_bar;
67 
68  typedef typename axi::axi4<axiCfg> axi4_;
69 
70  typedef typename axi4_::read::template master<>::ARPort axi_rd_master_ar;
71  typedef typename axi4_::read::template master<>::RPort axi_rd_master_r;
72  typedef typename axi4_::write::template master<>::AWPort axi_wr_master_aw;
73  typedef typename axi4_::write::template master<>::WPort axi_wr_master_w;
74  typedef typename axi4_::write::template master<>::BPort axi_wr_master_b;
75 
76  static const unsigned int log_numSlaves = nvhls::log2_ceil<numSlaves>::val + 1;
77 
78  // [ben] Unfortunately HLS cannot handle an nv_array of the master/slave wrapper classes.
79  // It will work fine in C but die mysteriously in Catapult 10.1b when methods of the
80  // bundled connections are accessed.
86  typename axi4_::read::template slave<> axi_rd_m;
87  typename axi4_::write::template slave<> axi_wr_m;
88 
89  sc_in<NVUINTW(numAddrBitsToInspect)> addrBound[numSlaves][2];
90 
91  SC_HAS_PROCESS(AxiSplitter);
92 
93  AxiSplitter(sc_module_name name)
94  : sc_module(name),
95  clk("clk"),
96  reset_bar("reset_bar"),
97  axi_rd_s_ar("axi_rd_s_ar"),
98  axi_rd_s_r("axi_rd_s_r"),
99  axi_wr_s_aw("axi_wr_s_aw"),
100  axi_wr_s_w("axi_wr_s_w"),
101  axi_wr_s_b("axi_wr_s_b"),
102  axi_rd_m("axi_rd_m"),
103  axi_wr_m("axi_wr_m")
104  {
105 
106  SC_THREAD(run_r);
107  sensitive << clk.pos();
108  async_reset_signal_is(reset_bar, false);
109 
110  SC_THREAD(run_w);
111  sensitive << clk.pos();
112  async_reset_signal_is(reset_bar, false);
113  }
114 
115  // As an optimization, responses with different IDs could be allowed
116  // through, since it is safe to return them out of order.
117 
118  void run_r() {
119 #pragma hls_unroll yes
120  for (int i=0; i<numSlaves; i++) {
121  axi_rd_s_ar[i].Reset();
122  axi_rd_s_r[i].Reset();
123  }
124  axi_rd_m.ar.Reset();
125  axi_rd_m.r.Reset();
126 
127  typename axi4_::AddrPayload AR_reg;
128  typename axi4_::ReadPayload R_reg;
129 
130  bool read_inFlight = 0;
131  NVUINTW(log_numSlaves) pushedTo = numSlaves;
132 
133  #pragma hls_pipeline_init_interval 1
134  #pragma pipeline_stall_mode flush
135  while (1) {
136  wait();
137 
138  switch (read_inFlight) {
139  case false:
140  if (axi_rd_m.ar.PopNB(AR_reg)) {
141  NVUINTW(numAddrBitsToInspect)
142  addr(static_cast<sc_uint<numAddrBitsToInspect> >(AR_reg.addr)); // Cast larger to smaller
143  pushedTo = numSlaves;
144  // TODO - refactor this so it can be unrolled
145  for (int i=0; i<numSlaves; i++) {
146  if (addr >= addrBound[i][0].read() && addr <= addrBound[i][1].read() && pushedTo == numSlaves) {
147  pushedTo = i;
148  }
149  }
150  if (default_output && pushedTo == numSlaves) {
151  pushedTo = numSlaves-1;
152  }
153  // If the address did not fall in any valid range, that's bad
154  NVHLS_ASSERT_MSG(pushedTo != numSlaves, "Read address did not fall into any output address range, and default output is not set");
155 
156  if (translate_addr)
157  AR_reg.addr -= addrBound[pushedTo][0].read();
158 
159  axi_rd_s_ar[pushedTo].Push(AR_reg);
160  read_inFlight = 1;
161  }
162  break;
163  case true:
164  if (axi_rd_s_r[pushedTo].PopNB(R_reg)) {
165  axi_rd_m.r.Push(R_reg);
166  if (R_reg.last == 1) read_inFlight = 0;
167  }
168  break;
169  }
170  }
171  }
172 
173  void run_w() {
174 #pragma hls_unroll yes
175  for (int i=0; i<numSlaves; i++) {
176  axi_wr_s_aw[i].Reset();
177  axi_wr_s_w[i].Reset();
178  axi_wr_s_b[i].Reset();
179  }
180  axi_wr_m.aw.Reset();
181  axi_wr_m.w.Reset();
182  axi_wr_m.b.Reset();
183 
184  typename axi4_::AddrPayload AW_reg;
185  typename axi4_::WritePayload W_reg;
186  typename axi4_::WRespPayload B_reg;
187 
188  NVUINTW(log_numSlaves) pushedTo = numSlaves;
189  enum {
190  IDLE = 0,
191  WRITE_INFLIGHT = 1,
192  RESP_INFLIGHT = 2,
193  };
194  NVUINT2 s = IDLE;
195 
196  #pragma hls_pipeline_init_interval 1
197  #pragma pipeline_stall_mode flush
198  while (1) {
199  wait();
200 
201  switch (s) {
202  case IDLE:
203  if (axi_wr_m.aw.PopNB(AW_reg)) {
204  NVUINTW(numAddrBitsToInspect)
205  addr(static_cast<sc_uint<numAddrBitsToInspect> >(AW_reg.addr)); // Cast larger to smaller
206  pushedTo = numSlaves;
207  // TODO - refactor this so it can be unrolled
208  for (int i=0; i<numSlaves; i++) {
209  if (addr >= addrBound[i][0].read() && addr <= addrBound[i][1].read() && pushedTo == numSlaves) {
210  pushedTo = i;
211  }
212  }
213  if (default_output && pushedTo == numSlaves) {
214  pushedTo = numSlaves-1;
215  }
216  NVHLS_ASSERT_MSG(pushedTo != numSlaves, "Write address did not fall into any output address range, and default output is not set");
217  if (translate_addr)
218  AW_reg.addr -= addrBound[pushedTo][0].read();
219 
220  axi_wr_s_aw[pushedTo].Push(AW_reg);
221  s = WRITE_INFLIGHT;
222  }
223  break;
224  case WRITE_INFLIGHT:
225  NVHLS_ASSERT_MSG(pushedTo != numSlaves, "Write address did not fall into any output address range, and default output is not set");
226  if (axi_wr_m.w.PopNB(W_reg)) {
227  axi_wr_s_w[pushedTo].Push(W_reg);
228  if (W_reg.last == 1) {
229  if (axiCfg::useWriteResponses) {
230  s = RESP_INFLIGHT;
231  } else {
232  s = IDLE;
233  }
234  }
235  }
236  break;
237  case RESP_INFLIGHT:
238  if (axiCfg::useWriteResponses) {
239  if (axi_wr_s_b[pushedTo].PopNB(B_reg)) {
240  axi_wr_m.b.Push(B_reg);
241  s = IDLE;
242  }
243  }
244  else {
245  NVHLS_ASSERT_MSG(0, "Should never reach this state if write responses are disabled");
246  }
247  break;
248  }
249  }
250  }
251 };
252 
253 #endif
A struct composed of the signals associated with an AXI write response.
Definition: axi4.h:236
NVUINTW(Wrapped< T >::width) TypeToNVUINT(T in)
Convert Type to NVUINT.
Definition: TypeToBits.h:115
A struct composed of the signals associated with AXI write data.
Definition: axi4.h:279
A struct composed of the signals associated with AXI read and write requests.
Definition: axi4.h:108
An n-way splitter that connects a single AXI master port to a multiple AXI slave ports.
Definition: AxiSplitter.h:62
A struct composed of the signals associated with an AXI read response.
Definition: axi4.h:180
The base axi4 class parameterized according a valid config.
Definition: axi4.h:58
Compute Celing of log2 of a constant.
Definition: nvhls_int.h:174
#define NVHLS_ASSERT_MSG(X, MSG)
Definition: nvhls_assert.h:116