MatchLib
Slave.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 __AXI_T_SLAVE__
18 #define __AXI_T_SLAVE__
19 
20 #include <systemc.h>
21 #include <ac_reset_signal_is.h>
22 
23 #include <axi/axi4.h>
24 #include <nvhls_connections.h>
25 #include <hls_globals.h>
26 
27 #include <queue>
28 #include <map>
29 #include <boost/assert.hpp>
30 #include <algorithm>
31 
44 template <typename axiCfg>
45 class Slave : public sc_module {
46  public:
47  static const int kDebugLevel = 0;
48  typedef axi::axi4<axiCfg> axi4_;
49 
50  typename axi4_::read::template slave<> if_rd;
51  typename axi4_::write::template slave<> if_wr;
52 
53  sc_in<bool> reset_bar;
54  sc_in<bool> clk;
55 
56  std::queue <typename axi4_::ReadPayload> rd_resp;
57  std::queue <typename axi4_::Addr> rd_resp_addr;
58  std::queue <typename axi4_::AddrPayload> wr_addr;
59  std::queue <typename axi4_::WritePayload> wr_data;
60  std::queue <typename axi4_::WRespPayload> wr_resp;
61 
62  std::map<typename axi4_::Addr, typename axi4_::Data> localMem;
63  std::map<typename axi4_::Addr, NVUINT8 > localMem_wstrb;
64  std::vector<typename axi4_::Addr> validReadAddresses;
65 
66  static const int bytesPerBeat = axi4_::DATA_WIDTH >> 3;
67 
68  SC_CTOR(Slave)
69  : if_rd("if_rd"), if_wr("if_wr"), reset_bar("reset_bar"), clk("clk") {
70  SC_THREAD(run_rd);
71  sensitive << clk.pos();
72  async_reset_signal_is(reset_bar, false);
73 
74  SC_THREAD(run_wr);
75  sensitive << clk.pos();
76  async_reset_signal_is(reset_bar, false);
77  }
78 
79  protected:
80  void run_rd() {
81  if_rd.reset();
82  unsigned int rdBeatInFlight = 0;
83 
84  while (1) {
85  wait();
86 
87  typename axi4_::AddrPayload rd_addr_pld;
88  if (if_rd.nb_aread(rd_addr_pld)) {
89  typename axi4_::Addr addr = rd_addr_pld.addr;
90  NVHLS_ASSERT_MSG(addr % bytesPerBeat == 0, "Addresses must be word aligned");
91  CDCOUT(sc_time_stamp() << " " << name() << " Received read request: ["
92  << rd_addr_pld << "]"
93  << endl, kDebugLevel);
94  NVUINTW(axi4_::ALEN_WIDTH) len = (axiCfg::useBurst ? rd_addr_pld.len : NVUINTW(axi4_::ALEN_WIDTH)(0));
95  for (unsigned int i=0; i<(len+1); i++) {
96  std::ostringstream msg;
97  msg << "\nError @" << sc_time_stamp() << " from " << name()
98  << ": Received a read request from an address that has not yet been written to"
99  << ", addr=" << hex << addr
100  << endl;
101  bool validAddr = false;
102  for (unsigned int j=0; j<validReadAddresses.size(); j++) {
103  if (validReadAddresses[j] == rd_addr_pld.addr) validAddr = true;
104  }
105  BOOST_ASSERT_MSG( validAddr, msg.str().c_str() );
106  typename axi4_::ReadPayload data_pld;
107  if (axiCfg::useWriteStrobes) {
108  for (int k=0; k<axi4_::WSTRB_WIDTH; k++) {
109  if (localMem_wstrb.find(addr+k) != localMem_wstrb.end()) {
110  data_pld.data = nvhls::set_slc(data_pld.data, localMem_wstrb[addr+k], 8*k);
111  } else {
112  data_pld.data = nvhls::set_slc(data_pld.data, NVUINT8(0), 8*k);
113  }
114  }
115  } else {
116  data_pld.data = localMem[addr];
117  }
118  data_pld.resp = axi4_::Enc::XRESP::OKAY;
119  data_pld.id = rd_addr_pld.id;
120  data_pld.last = (i == len);
121  rd_resp.push(data_pld);
122  rd_resp_addr.push(addr);
123  addr += bytesPerBeat;
124  }
125  }
126 
127  if (!rd_resp.empty()) {
128  typename axi4_::ReadPayload data_pld;
129  data_pld = rd_resp.front();
130  typename axi4_::Addr addr = rd_resp_addr.front();
131  if (if_rd.nb_rwrite(data_pld)) {
132  CDCOUT(sc_time_stamp() << " " << name() << " Returned read data:"
133  << " data=[" << data_pld << "]"
134  << " addr=" << hex << addr.to_uint64()
135  << " beat=" << dec << (axiCfg::useBurst ? static_cast< sc_uint<32> >(rdBeatInFlight++) : "N/A")
136  << endl, kDebugLevel);
137  if (data_pld.last == 1) {
138  rdBeatInFlight = 0;
139  }
140  rd_resp.pop();
141  rd_resp_addr.pop();
142  }
143  }
144  }
145  }
146 
147  void run_wr() {
148  if_wr.reset();
149 
150  typename axi4_::WRespPayload resp_pld;
151  typename axi4_::AddrPayload wr_addr_pld;
152  typename axi4_::WritePayload wr_data_pld;
153  typename axi4_::AddrPayload wr_addr_pld_out;
154  typename axi4_::WritePayload wr_data_pld_out;
155  unsigned int wrBeatInFlight = 0;
156  bool first_beat = 1;
157  typename axi4_::Addr wresp_addr;
158 
159  while (1) {
160  wait();
161 
162  // Send a write response out of the local queue
163  if (axiCfg::useWriteResponses) {
164  if (!wr_resp.empty()) {
165  resp_pld = wr_resp.front();
166  if (if_wr.nb_bwrite(resp_pld)) {
167  wr_resp.pop();
168  CDCOUT(sc_time_stamp() << " " << name() << " Sent write response: ["
169  << resp_pld << "]"
170  << endl, kDebugLevel);
171  }
172  }
173  }
174 
175  // Grab a write request (addr) and put it in the local queue
176  if (if_wr.aw.PopNB(wr_addr_pld)) {
177  NVHLS_ASSERT_MSG(wr_addr_pld.addr.to_uint64() % bytesPerBeat == 0, "Addresses must be word aligned");
178  wr_addr.push(wr_addr_pld);
179  CDCOUT(sc_time_stamp() << " " << name() << " Received write request: ["
180  << wr_addr_pld << "]"
181  << endl, kDebugLevel);
182  }
183 
184  // Grab a write request (data) and put it in the local queue
185  if (if_wr.w.PopNB(wr_data_pld)) {
186  wr_data.push(wr_data_pld);
187  CDCOUT(sc_time_stamp() << " " << name() << " Received write data:"
188  << " data=[" << wr_data_pld << "]"
189  << " beat=" << dec << (axiCfg::useBurst ? static_cast< sc_uint<32> >(wrBeatInFlight++) : "N/A")
190  << endl, kDebugLevel);
191  if (wr_data_pld.last == 1) {
192  wrBeatInFlight = 0;
193  }
194  }
195 
196  // Handle a write request in the local queues
197  if (!wr_addr.empty() & !wr_data.empty()) {
198  if (first_beat) {
199  wr_addr_pld_out = wr_addr.front();
200  wresp_addr = wr_addr_pld_out.addr;
201  first_beat = 0;
202  }
203  wr_data_pld_out = wr_data.front(); wr_data.pop();
204  // Store the data
205  if (axiCfg::useWriteStrobes) {
206  std::ostringstream msg;
207  msg << "\nError @" << sc_time_stamp() << " from " << name()
208  << ": Wstrb cannot be all zeros" << endl;
209  BOOST_ASSERT_MSG( wr_data_pld_out.wstrb != 0, msg.str().c_str() );
210  for (int j=0; j<axi4_::WSTRB_WIDTH; j++) {
211  if (wr_data_pld_out.wstrb[j] == 1) {
212  localMem_wstrb[wresp_addr+j] = nvhls::get_slc<8>(wr_data_pld_out.data, 8*j);
213  }
214  }
215  } else {
216  localMem[wresp_addr] = wr_data_pld_out.data;
217  }
218  validReadAddresses.push_back(wresp_addr);
219  wresp_addr += bytesPerBeat;
220  if (wr_data_pld_out.last == 1) {
221  wr_addr.pop();
222  first_beat = 1;
223  // Generate a response
224  if (axiCfg::useWriteResponses) {
225  resp_pld.resp = axi4_::Enc::XRESP::OKAY;
226  resp_pld.id = wr_addr_pld_out.id;
227  wr_resp.push(resp_pld);
228  }
229  }
230  }
231  }
232  }
233 };
234 
235 #endif
type1 set_slc(type1 X, type2 Y, const unsigned int i)
Function that replaces slice of bits.
Definition: nvhls_int.h:387
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
A struct composed of the signals associated with an AXI read response.
Definition: axi4.h:180
#define CDCOUT(x, y)
Definition: hls_globals.h:73
The base axi4 class parameterized according a valid config.
Definition: axi4.h:58
#define NVHLS_ASSERT_MSG(X, MSG)
Definition: nvhls_assert.h:116
An AXI slave for use in a testbench.
Definition: Slave.h:45