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