MatchLib
SlaveFromFile.h
1 /*
2  * Copyright (c) 2017-2020, 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_FROM_FILE__
18 #define __AXI_T_SLAVE_FROM_FILE__
19 
20 #include <systemc.h>
21 #include <ac_reset_signal_is.h>
22 
23 #include <axi/axi4.h>
24 #include <axi/testbench/CSVFileReader.h>
25 #include <nvhls_connections.h>
26 #include <hls_globals.h>
27 
28 #include <queue>
29 #include <map>
30 #include <boost/assert.hpp>
31 #include <algorithm>
32 #include <string>
33 #include <sstream>
34 
48 template <typename axiCfg> class SlaveFromFile : public sc_module {
49  public:
50  static const int kDebugLevel = 0;
51  typedef axi::axi4<axiCfg> axi4_;
52 
53  typename axi4_::read::template slave<> if_rd;
54  typename axi4_::write::template slave<> if_wr;
55 
56  sc_in<bool> reset_bar;
57  sc_in<bool> clk;
58 
59  std::queue <typename axi4_::ReadPayload> rd_resp;
60  std::queue <typename axi4_::Addr> rd_resp_addr;
61  std::queue <typename axi4_::AddrPayload> wr_addr;
62  std::queue <typename axi4_::WritePayload> wr_data;
63  std::queue <typename axi4_::WRespPayload> wr_resp;
64 
65  std::map<typename axi4_::Addr, typename axi4_::Data> localMem;
66  std::map<typename axi4_::Addr, NVUINT8> localMem_wstrb;
67  std::vector<typename axi4_::Addr> validReadAddresses;
68 
69  typename axi4_::WritePayload load_data_pld;
70 
71  static const int bytesPerBeat = axi4_::DATA_WIDTH >> 3;
72 
73  SC_HAS_PROCESS(SlaveFromFile);
74 
75  SlaveFromFile(sc_module_name name_, std::string filename="mem.csv")
76  : sc_module(name_), if_rd("if_rd"), if_wr("if_wr"), reset_bar("reset_bar"), clk("clk") {
77 
78  CSVFileReader reader(filename);
79  std::vector< std::vector<std::string> > dataList = reader.readCSV();
80  for (unsigned int i=0; i < dataList.size(); i++) {
81  std::vector<std::string> vec = dataList[i];
82  NVHLS_ASSERT_MSG(vec.size() == 2, "Each request must have two elements");
83  std::stringstream ss;
84  sc_uint<axi4_::ADDR_WIDTH> addr_sc_uint;
85  ss << hex << vec[0];
86  ss >> addr_sc_uint;
87  std::stringstream ss_data;
88  sc_biguint<axi4_::DATA_WIDTH> data;
89  ss_data << hex << vec[1];
90  ss_data >> data;
91  load_data_pld.data = TypeToNVUINT(data);
92  typename axi4_::Addr addr = static_cast<typename axi4_::Addr>(addr_sc_uint);
93  if (axiCfg::useWriteStrobes) {
94  for (int j=0; j<axi4_::WSTRB_WIDTH; j++) {
95  localMem_wstrb[addr+j] = nvhls::get_slc<8>(load_data_pld.data, 8*j);
96  }
97  } else {
98  localMem[addr] = load_data_pld.data;
99  }
100  validReadAddresses.push_back(addr);
101  }
102 
103  SC_THREAD(run_rd);
104  sensitive << clk.pos();
105  async_reset_signal_is(reset_bar, false);
106 
107  SC_THREAD(run_wr);
108  sensitive << clk.pos();
109  async_reset_signal_is(reset_bar, false);
110  }
111 
112 protected:
113  void run_rd() {
114  if_rd.reset();
115  unsigned int rdBeatInFlight = 0;
116 
117  while (1) {
118  wait();
119 
120  typename axi4_::AddrPayload rd_addr_pld;
121  if (if_rd.nb_aread(rd_addr_pld)) {
122  typename axi4_::Addr addr = rd_addr_pld.addr;
123  CDCOUT(sc_time_stamp() << " " << name() << " Received read request: ["
124  << rd_addr_pld << "]"
125  << endl, kDebugLevel);
126  NVUINTW(axi4_::ALEN_WIDTH) len = (axiCfg::useBurst ? rd_addr_pld.len : NVUINTW(axi4_::ALEN_WIDTH)(0));
127  for (unsigned int i=0; i<(len+1); i++) {
128  std::ostringstream msg;
129  msg << "\nError @" << sc_time_stamp() << " from " << name()
130  << ": Received a read request from an address that has not yet been written to"
131  << ", addr=" << hex << addr
132  << endl;
133  bool validAddr = false;
134  for (unsigned int j=0; j<validReadAddresses.size(); j++) {
135  if (validReadAddresses[j] == rd_addr_pld.addr) validAddr = true;
136  }
137  BOOST_ASSERT_MSG( validAddr, msg.str().c_str() );
138  typename axi4_::ReadPayload data_pld;
139  if (axiCfg::useWriteStrobes) {
140  for (int k=0; k<axi4_::WSTRB_WIDTH; k++) {
141  if (localMem_wstrb.find(addr+k) != localMem_wstrb.end()) {
142  data_pld.data = nvhls::set_slc(data_pld.data, localMem_wstrb[addr+k], 8*k);
143  } else {
144  data_pld.data = nvhls::set_slc(data_pld.data, NVUINT8(0), 8*k);
145  }
146  }
147  } else {
148  data_pld.data = localMem[addr];
149  }
150  data_pld.resp = axi4_::Enc::XRESP::OKAY;
151  data_pld.id = rd_addr_pld.id;
152  data_pld.last = (i == len);
153  rd_resp.push(data_pld);
154  rd_resp_addr.push(addr);
155  addr += bytesPerBeat;
156  }
157  }
158 
159  if (!rd_resp.empty()) {
160  typename axi4_::ReadPayload data_pld;
161  data_pld = rd_resp.front();
162  typename axi4_::Addr addr = rd_resp_addr.front();
163  if (if_rd.nb_rwrite(data_pld)) {
164  CDCOUT(sc_time_stamp() << " " << name() << " Returned read data:"
165  << " data=[" << data_pld << "]"
166  << " addr=" << hex << addr.to_uint64()
167  << " beat=" << dec << (axiCfg::useBurst ? static_cast< sc_uint<32> >(rdBeatInFlight++) : "N/A")
168  << endl, kDebugLevel);
169  if (data_pld.last == 1) {
170  rdBeatInFlight = 0;
171  }
172  rd_resp.pop();
173  rd_resp_addr.pop();
174  }
175  }
176  }
177  }
178 
179  void run_wr() {
180  if_wr.reset();
181 
182  typename axi4_::WRespPayload resp_pld;
183  typename axi4_::AddrPayload wr_addr_pld;
184  typename axi4_::WritePayload wr_data_pld;
185  unsigned int wrBeatInFlight = 0;
186  bool first_beat = 1;
187  typename axi4_::Addr wresp_addr;
188 
189  while (1) {
190  wait();
191 
192  // Send a write response out of the local queue
193  if (axiCfg::useWriteResponses) {
194  if (!wr_resp.empty()) {
195  resp_pld = wr_resp.front();
196  if (if_wr.nb_bwrite(resp_pld)) {
197  wr_resp.pop();
198  CDCOUT(sc_time_stamp() << " " << name() << " Sent write response"
199  << endl, kDebugLevel);
200  }
201  }
202  }
203 
204  // Grab a write request (addr) and put it in the local queue
205  if (if_wr.aw.PopNB(wr_addr_pld)) {
206  wr_addr.push(wr_addr_pld);
207  CDCOUT(sc_time_stamp() << " " << name() << " Received write request:"
208  << wr_addr_pld << endl, kDebugLevel);
209  }
210 
211  // Grab a write request (data) and put it in the local queue
212  if (if_wr.w.PopNB(wr_data_pld)) {
213  wr_data.push(wr_data_pld);
214  CDCOUT(sc_time_stamp() << " " << name() << " Received write data:"
215  << " data=[" << wr_data_pld << "]"
216  << " beat=" << dec << (axiCfg::useBurst ? static_cast< sc_uint<32> >(wrBeatInFlight++) : "N/A")
217  << endl, kDebugLevel);
218  if (wr_data_pld.last == 1) {
219  wrBeatInFlight = 0;
220  }
221  }
222 
223  // Handle a write request in the local queues
224  if (!wr_addr.empty() & !wr_data.empty()) {
225  if (first_beat) {
226  wr_addr_pld = wr_addr.front();
227  wresp_addr = wr_addr_pld.addr;
228  first_beat = 0;
229  }
230  wr_data_pld = wr_data.front(); wr_data.pop();
231  // Store the data
232  if (axiCfg::useWriteStrobes) {
233  std::ostringstream msg;
234  msg << "\nError @" << sc_time_stamp() << " from " << name()
235  << ": Wstrb cannot be all zeros" << endl;
236  BOOST_ASSERT_MSG( wr_data_pld.wstrb != 0, msg.str().c_str() );
237  for (int j=0; j<axi4_::WSTRB_WIDTH; j++) {
238  if (wr_data_pld.wstrb[j] == 1) {
239  localMem_wstrb[wresp_addr+j] = nvhls::get_slc<8>(wr_data_pld.data, 8*j);
240  }
241  }
242  } else {
243  localMem[wresp_addr] = wr_data_pld.data;
244  }
245  validReadAddresses.push_back(wresp_addr);
246  wresp_addr += bytesPerBeat;
247  if (wr_data_pld.last == 1) {
248  wr_addr.pop();
249  first_beat = 1;
250  // Generate a response
251  if (axiCfg::useWriteResponses) {
252  resp_pld.resp = axi4_::Enc::XRESP::OKAY;
253  resp_pld.id = wr_addr_pld.id;
254  wr_resp.push(resp_pld);
255  }
256  }
257  }
258  }
259  }
260 };
261 
262 #endif
An AXI slave with its memory prepopulated from a file for use in testbenches.
Definition: SlaveFromFile.h:48
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
A helper class to read CSV files.
Definition: CSVFileReader.h:37