MatchLib
AxiSplitter.h
1 /*
2  * Copyright (c) 2018-2024, 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 numSubordinates, 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 manager<>::ARPort axi_rd_manager_ar;
71  typedef typename axi4_::read::template manager<>::RPort axi_rd_manager_r;
72  typedef typename axi4_::write::template manager<>::AWPort axi_wr_manager_aw;
73  typedef typename axi4_::write::template manager<>::WPort axi_wr_manager_w;
74  typedef typename axi4_::write::template manager<>::BPort axi_wr_manager_b;
75 
76  static const unsigned int log_numSubordinates = nvhls::log2_ceil<numSubordinates>::val + 1;
77 
78  // [ben] Unfortunately HLS cannot handle an nv_array of the manager/subordinate 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 subordinate<> axi_rd_m;
87  typename axi4_::write::template subordinate<> axi_wr_m;
88 
89  sc_in<NVUINTW(numAddrBitsToInspect)> addrBound[numSubordinates][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<numSubordinates; 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_numSubordinates) pushedTo = numSubordinates;
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 = numSubordinates;
144  // TODO - refactor this so it can be unrolled
145  for (int i=0; i<numSubordinates; i++) {
146  if (addr >= addrBound[i][0].read() && addr <= addrBound[i][1].read() && pushedTo == numSubordinates) {
147  pushedTo = i;
148  }
149  }
150  if (default_output && pushedTo == numSubordinates) {
151  pushedTo = numSubordinates-1;
152  }
153  // If the address did not fall in any valid range, that's bad
154  NVHLS_ASSERT_MSG(pushedTo != numSubordinates, "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<numSubordinates; 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_numSubordinates) pushedTo = numSubordinates;
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 = numSubordinates;
207  // TODO - refactor this so it can be unrolled
208  for (int i=0; i<numSubordinates; i++) {
209  if (addr >= addrBound[i][0].read() && addr <= addrBound[i][1].read() && pushedTo == numSubordinates) {
210  pushedTo = i;
211  }
212  }
213  if (default_output && pushedTo == numSubordinates) {
214  pushedTo = numSubordinates-1;
215  }
216  NVHLS_ASSERT_MSG(pushedTo != numSubordinates, "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 != numSubordinates, "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
An n-way splitter that connects a single AXI manager port to a multiple AXI subordinate ports.
Definition: AxiSplitter.h:62
The base axi4 class parameterized according a valid config.
Definition: axi4.h:64
#define NVHLS_ASSERT_MSG(X, MSG)
Definition: nvhls_assert.h:135
NVUINTW(Wrapped< T >::width) TypeToNVUINT(T in)
Convert Type to NVUINT.
Definition: TypeToBits.h:115
A struct composed of the signals associated with AXI read and write requests.
Definition: axi4.h:114
A struct composed of the signals associated with an AXI read response.
Definition: axi4.h:157
A struct composed of the signals associated with an AXI write response.
Definition: axi4.h:190
A struct composed of the signals associated with AXI write data.
Definition: axi4.h:215
Compute Celing of log2 of a constant.
Definition: nvhls_int.h:174