MatchLib
AxiManagerGate.h
1 /*
2  * Copyright (c) 2017-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 
17 #ifndef __AXIMANAGERGATE_H__
18 #define __AXIMANAGERGATE_H__
19 
20 #include <systemc.h>
21 #include <ac_reset_signal_is.h>
22 #include <axi/axi4.h>
23 #include <mem_array.h>
24 #include <fifo.h>
25 #include <ReorderBuf.h>
26 #include <axi/AxiManagerGate/ReorderBufWBeats.h>
27 #include <nvhls_connections.h>
28 #include <nvhls_assert.h>
29 #include <axi/AxiManagerGate/AxiManagerGateIf.h>
30 
59 template <typename Cfg, int ROBDepth = 8, int MaxInFlightTrans = 4>
60 class AxiManagerGate : public sc_module {
61  private:
62  typedef axi::axi4<Cfg> axi4_;
63 
64  typedef NVUINTW(axi4_::DATA_WIDTH) Data;
65 
66  ReorderBuf<WrResp<Cfg>, ROBDepth, MaxInFlightTrans> wr_rob;
67  ReorderBufWBeats<RdResp<Cfg>, ROBDepth, MaxInFlightTrans> rd_rob;
68 
69  FIFO<RdRequest<Cfg>, 4> rdReqFifo;
70  FIFO<WrRequest<Cfg>, 4> wrReqFifo;
71 
72  static_assert(MaxInFlightTrans <= (1 << axi4_::ID_WIDTH) , "Number of inflight transactions cannot exceed number of unique IDs");
73 
74  public:
75  static const int kDebugLevel = 2;
76  typename axi4_::read::template manager<> if_rd;
77  typename axi4_::write::template manager<> if_wr;
78 
79  sc_in<bool> reset_bar;
80  sc_in<bool> clk;
81  Connections::In<WrRequest<Cfg> > wrRequestIn;
82  Connections::Out<WrResp<Cfg> > wrRespOut;
83  Connections::In<RdRequest<Cfg> > rdRequestIn;
84  Connections::Out<RdResp<Cfg> > rdRespOut;
85 
86  SC_CTOR(AxiManagerGate)
87  : if_rd("if_rd"), if_wr("if_wr"), reset_bar("reset_bar"), clk("clk") {
88  SC_THREAD(run_rd);
89  sensitive << clk.pos();
90  async_reset_signal_is(reset_bar, false);
91 
92  SC_THREAD(run_wr);
93  sensitive << clk.pos();
94  async_reset_signal_is(reset_bar, false);
95  }
96 
97  protected:
98  typedef sc_uint<axi4_::ID_WIDTH> Id;
99 
100  void run_wr() {
101 
102  if_wr.reset();
103  wrRequestIn.Reset();
104  wrRespOut.Reset();
105  wr_rob.reset();
106  wrReqFifo.reset();
107 
108  bool addr_sent = false;
109  WrRequest<Cfg> wrRequest;
110  bool wrRequestValid = false;
111  Id wrRequestId;
112  bool wrRequestIdValid = false;
113  bool isBurstInFlight = false;
114 
115  #pragma hls_pipeline_init_interval 1
116  #pragma pipeline_stall_mode flush
117  while (1) {
118  wait();
119  CDCOUT("@" << sc_time_stamp() << "\t wr_rob is empty? "
120  << wr_rob.isEmpty() << endl, kDebugLevel);
121  // send response
122  if (wr_rob.topResponseReady()) {
123  WrResp<Cfg> wrResp;
124  wrResp = wr_rob.popResponse();
125 
126  CDCOUT("@" << sc_time_stamp()
127  << "\t\t Pop first response from rob and push it into host"
128  << "\tresp = " << wrResp.resp << endl, kDebugLevel);
129 
130  wrRespOut.Push(wrResp); // I expect the other side to be always ready,
131  // therefore using blocking push here
132  CDCOUT("@" << sc_time_stamp() << "\t\t Done pushing resp to host"
133  << endl, kDebugLevel);
134  }
135 
136  WrRequest<Cfg> wrRequest_local = wrRequest;
137  bool wrRequestValid_local = wrRequestValid;
138  Id wrRequestId_local = wrRequestId;
139  bool wrRequestIdValid_local = wrRequestIdValid;
140  bool wrReqFifo_isFull = wrReqFifo.isFull();
141 
142  // receive requests
143  if (!wrRequestValid_local && !wrReqFifo.isEmpty()) {
144  wrRequest_local = wrReqFifo.pop();
145  wrRequestValid_local = true;
146  wrRequestIdValid_local = false;
147 
148  CDCOUT("@" << sc_time_stamp() << "\t\t wr:pop wrReqFifo" << endl, kDebugLevel);
149  }
150 
151  // receive responses
152  {
153  typename axi4_::WRespPayload resp_pld;
154  CDCOUT("@" << sc_time_stamp() << "\t\t Receiving resp from subordinate"
155  << endl, kDebugLevel);
156  if (if_wr.b.PopNB(resp_pld)) {
157  WrResp<Cfg> wrResp;
158  wrResp.resp = resp_pld.resp;
159 
160  CDCOUT("@" << sc_time_stamp() << "\t\t wr:Post response to rob"
161  << "\tid = " << resp_pld.id << "\tresp = " << resp_pld.resp
162  << endl, kDebugLevel);
163  wr_rob.addResponse(static_cast<sc_uint<axi4_::ID_WIDTH> >(resp_pld.id), wrResp);
164  }
165  }
166 
167  if (wrRequestValid_local) {
168  bool isBurstInFlight_local = isBurstInFlight;
169  wrRequestIdValid_local =
170  wrRequestIdValid_local || isBurstInFlight_local;
171 
172  // allocate a new id
173  if (!wrRequestIdValid_local && wr_rob.canAcceptRequest()) {
174  wrRequestId_local = wr_rob.addRequest();
175  wrRequestIdValid_local = true;
176  CDCOUT("@" << sc_time_stamp()
177  << "\t\t wr:new id allocated = " << wrRequestId_local << endl, kDebugLevel);
178  }
179 
180  if (wrRequestIdValid_local) {
181  NVHLS_ASSERT_MSG(wrRequestValid_local, "Should be impossible to reach this state");
182 
183  bool addr_sent_local = addr_sent;
184  if (!addr_sent_local) {
185  // send addr
186  typename axi4_::AddrPayload addr_pld;
187  wrRequest_local.copyToAddrPayload(addr_pld);
188  addr_pld.id = static_cast<typename axi4_::Id>(wrRequestId_local);
189 
190  addr_sent_local = if_wr.aw.PushNB(addr_pld);
191  CDCOUT("@" << sc_time_stamp() << "\t\t wr:sending addr pass? "
192  << addr_sent_local << "\taddr = " << wrRequest_local.addr
193  << "\tid = " << wrRequestId_local
194  << "\tlen = " << wrRequest_local.len
195  << "\tsize = " << wrRequest_local.size
196  << "\tburst = " << wrRequest_local.burst << endl, kDebugLevel);
197  } else {
198  // send data
199  typename axi4_::WritePayload write_pld;
200  write_pld.data = wrRequest_local.data;
201  write_pld.last = wrRequest_local.last;
202  write_pld.wstrb = ~0;
203 
204  bool pushed;
205  pushed = if_wr.w.PushNB(write_pld);
206  wrRequestValid_local = wrRequestValid_local && !pushed;
207  wrRequestIdValid_local = wrRequestIdValid_local && !pushed;
208  CDCOUT("@" << sc_time_stamp() << "\t\t sending data pass? "
209  << !addr_sent_local << "\tdata = " << wrRequest_local.data
210  << "\tlast = " << wrRequest_local.last << endl, kDebugLevel);
211  isBurstInFlight_local = (wrRequest_local.last != 1);
212 
213  // if this is a burst, and not the last beat next time we need to
214  // keep sending data, not addr yet
215  bool sendWNext = isBurstInFlight_local || !pushed;
216  // addr_sent_local==false will mean the next cycle that addr has to
217  // be sent
218  addr_sent_local = sendWNext;
219  }
220  addr_sent = addr_sent_local;
221  }
222  isBurstInFlight = isBurstInFlight_local;
223  }
224 
225  wrRequest = wrRequest_local;
226  wrRequestValid = wrRequestValid_local;
227  wrRequestId = wrRequestId_local;
228  wrRequestIdValid = wrRequestIdValid_local;
229 
230  if (!wrReqFifo_isFull) {
231  WrRequest<Cfg> wrRequest1;
232  // receive requests
233  if (wrRequestIn.PopNB(wrRequest1)) {
234  wrReqFifo.push(wrRequest1);
235 
236  CDCOUT("@" << sc_time_stamp() << "\t\t wr:m got request!" << endl, kDebugLevel);
237  }
238  }
239  }
240  }
241 
242  void run_rd() {
243  rdRequestIn.Reset();
244  rdRespOut.Reset();
245  rd_rob.reset();
246  if_rd.reset();
247  rdReqFifo.reset();
248 
249  RdRequest<Cfg> rdRequest;
250  bool rdRequestValid = false;
251  Id rdRequestId;
252  bool rdRequestIdValid = false;
253  bool rdBurstInFlight = false;
254  bool rdReceivingBurstBeats = false;
255 
256  #pragma hls_pipeline_init_interval 1
257  #pragma pipeline_stall_mode flush
258  while (1) {
259  wait();
260 
261  CDCOUT("@" << sc_time_stamp() << "\t rd_rob is empty? "
262  << rd_rob.isEmpty() << endl, kDebugLevel);
263  // send response
264  if (rd_rob.topResponseReady()) {
265  RdResp<Cfg> rdResp;
266  rdResp = rd_rob.popResponse();
267 
268  CDCOUT("@" << sc_time_stamp()
269  << "\t\t rd:Pop first response from rob and push it into host"
270  //<< "\tresp = " << rdResp
271  << endl, kDebugLevel);
272 
273  rdRespOut.Push(rdResp); // I expect the other side to be always ready,
274  // therefore using blocking push here
275  CDCOUT("@" << sc_time_stamp() << "\t\t rd:Done pushing resp to host"
276  << endl, kDebugLevel);
277  }
278 
279  RdRequest<Cfg> rdRequest_local = rdRequest;
280  bool rdRequestValid_local = rdRequestValid;
281  Id rdRequestId_local = rdRequestId;
282  bool rdRequestIdValid_local = rdRequestIdValid;
283  bool rdReqFifo_isFull = rdReqFifo.isFull();
284  bool rdBurstInFlight_local = rdBurstInFlight;
285 
286  if (!rdRequestValid_local && !rdReqFifo.isEmpty()) {
287  rdRequest_local = rdReqFifo.pop();
288  rdRequestValid_local = true;
289  rdRequestIdValid_local = false;
290  CDCOUT("@" << sc_time_stamp() << "\t\t rd:pop rdReqFifo" << endl, kDebugLevel);
291  }
292 
293  if (rdRequestValid_local) {
294 
295  // rdRequest_local.len!=0 is a multi beat (burst) read
296  // we will wait for all previous read transactions to complete first
297  // otherwise we just need a slot in rob
298 
299  bool isBurst = (rdRequest_local.len != 0);
300  bool robReady =
301  (isBurst ? rd_rob.isEmpty() : rd_rob.canAcceptRequest());
302 
303  if (!rdRequestIdValid_local && robReady && !rdBurstInFlight_local) {
304  rdRequestId_local = rd_rob.addRequest();
305  rdRequestIdValid_local = true;
306  rdBurstInFlight_local = isBurst;
307  CDCOUT("@" << sc_time_stamp()
308  << "\t\t rd:new id allocated = " << rdRequestId << endl, kDebugLevel);
309  }
310 
311  if (rdRequestIdValid_local) {
312  typename axi4_::AddrPayload addr_pld;
313  rdRequest_local.copyToAddrPayload(addr_pld);
314  addr_pld.id = static_cast<typename axi4_::Id>(rdRequestId_local);
315 
316  bool pushed = if_rd.ar.PushNB(addr_pld);
317  rdRequestValid_local = !pushed;
318  rdRequestIdValid_local = !pushed;
319  }
320  }
321 
322  rdRequest = rdRequest_local;
323  rdRequestValid = rdRequestValid_local;
324  rdRequestId = rdRequestId_local;
325  rdRequestIdValid = rdRequestIdValid_local;
326 
327  if (!rdReqFifo_isFull) {
328  RdRequest<Cfg> rdRequest1;
329  // receive requests
330  if (rdRequestIn.PopNB(rdRequest1)) {
331  rdReqFifo.push(rdRequest1);
332 
333  CDCOUT("@" << sc_time_stamp() << "\t\t rd:m got request ? "
334  << rdRequestValid << endl, kDebugLevel);
335  }
336  }
337 
338  {
339  bool rdReceivingBurstBeats_local = rdReceivingBurstBeats;
340  typename axi4_::ReadPayload data_pld;
341  // for single beat transactions rob is guaranteed to have an entry for
342  // response
343  // but for multi beat, starting from the second beat we don't know and
344  // have to check it
345  if (!rdReceivingBurstBeats_local || rd_rob.canReceiveBeats()) {
346  if (if_rd.r.PopNB(data_pld)) {
347  RdResp<Cfg> rdResp;
348  rdResp.data = data_pld.data;
349  rdResp.resp = data_pld.resp;
350  rdResp.last = data_pld.last;
351 
352  if (!rdReceivingBurstBeats_local) {
353  rd_rob.addResponse(static_cast<sc_uint<axi4_::ID_WIDTH> >(data_pld.id), rdResp);
354  } else {
355  rd_rob.addBeat(rdResp);
356  if (static_cast<sc_uint<1> >(data_pld.last) == 1) {
357  // if this is the last beat, return to normal operation mode
358  rdBurstInFlight_local = false;
359  }
360  }
361 
362  // subsequent beats (after the first one will have to know to use a
363  // different api to be pushed into Rob)
364  rdReceivingBurstBeats_local = rdBurstInFlight_local;
365  }
366  }
367  rdReceivingBurstBeats = rdReceivingBurstBeats_local;
368  }
369  rdBurstInFlight = rdBurstInFlight_local;
370  }
371  }
372 };
373 
374 #endif
An AXI manager that converts from a simple request/response interface to AXI with reordering support.
Configurable FIFO class.
Definition: fifo.h:65
Reorder Buffer that allows out-of-order writes to queue and in-order reads.
Definition: ReorderBuf.h:68
An extension of ReorderBuf that allows one entry to contain multiple beats of data.
The base axi4 class parameterized according a valid config.
Definition: axi4.h:64
#define NVHLS_ASSERT_MSG(X, MSG)
Definition: nvhls_assert.h:135
#define CDCOUT(x, y)
Definition: hls_globals.h:73
The struct for read requests for AxiManagerGate.
The struct for read responses for AxiManagerGate.
The struct for write requests for AxiManagerGate.
The struct for write responses for AxiManagerGate.
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