MatchLib
All Classes Namespaces Files Functions Modules Pages
Manager.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#ifndef __AXI_T_MANAGER__
17#define __AXI_T_MANAGER__
18
19#include <systemc.h>
20#include <ac_reset_signal_is.h>
21
22#include <axi/axi4.h>
23#include <nvhls_connections.h>
24#include <hls_globals.h>
25
26#include <queue>
27#include <string>
28#include <sstream>
29#include <vector>
30#include <map>
31#include <math.h>
32#include <boost/assert.hpp>
33
34#include <boost/random/mersenne_twister.hpp>
35#include <boost/random/uniform_int_distribution.hpp>
36#include <algorithm>
37
50struct managerCfg {
51 enum {
52 numWrites = 100,
53 numReads = 100,
54 readDelay = 0,
55 seed = 0,
56 };
57 // enum only supports 32-bit types, so use static 64-bit types as needed (e.g., for >32bit addresses)
58 static const uint64_t addrBoundLower = 0;
59 static const uint64_t addrBoundUpper = 0xFFFFFFFF;
60};
61
86template <typename axiCfg, typename cfg>
87class Manager : public sc_module {
88 BOOST_STATIC_ASSERT_MSG(axiCfg::useWriteResponses || cfg::numReads == 0 || cfg::readDelay != 0,
89 "Must use a substantial read delay if reading without write responses");
90 public:
91 static const int kDebugLevel = 1;
93
94 typename axi4_::read::template manager<> if_rd;
95 typename axi4_::write::template manager<> if_wr;
96
97 sc_in<bool> reset_bar;
98 sc_in<bool> clk;
99
100 std::map<typename axi4_::Addr, typename axi4_::Data> localMem;
101 std::map<typename axi4_::Addr, NVUINT8 > localMem_wstrb;
102 std::vector<typename axi4_::Addr> validReadAddresses;
103 std::vector<typename axi4_::Addr> validReadAddresses_q;
104 std::vector< long long > validReadAddresses_ctr;
105
106 static const int bytesPerBeat = axi4_::DATA_WIDTH >> 3;
107 static const bool wResp = axiCfg::useWriteResponses;
108
109 sc_out<bool> done;
110
111 SC_CTOR(Manager)
112 : if_rd("if_rd"), if_wr("if_wr"), reset_bar("reset_bar"), clk("clk") {
113
114 SC_THREAD(run);
115 sensitive << clk.pos();
116 async_reset_signal_is(reset_bar, false);
117 }
118
119 protected:
120 void run() {
121 static const int bytesPerWord = axi4_::DATA_WIDTH >> 3;
122 static const int axiAddrBitsPerWord = nvhls::log2_ceil<bytesPerWord>::val;
123 // Workaround for useBurst=0, which sets ALEN field to 0 width
124 static const int ALEN_W = axiCfg::useBurst != 0 ? axi4_::ALEN_WIDTH : 1;
125 static const int WSTRB_W = axiCfg::useWriteStrobes != 0 ? axi4_::WSTRB_WIDTH : 1;
126 std::queue <typename axi4_::Addr> raddr_queue;
127 std::queue < NVUINTW(ALEN_W) > rlen_queue;
128 std::queue <typename axi4_::Addr> waddr_queue;
129 std::queue < NVUINTW(ALEN_W) > wlen_queue;
130 typename axi4_::Data check_data;
131 NVUINT8 check_data_wstrb;
132 NVUINT8 rcv_data_wstrb;
133 typename axi4_::AddrPayload addr_pld;
134 typename axi4_::ReadPayload data_pld;
135
136 // Follow the same priority as nvhls_rand: environment, then preprocessor define, then config
137 unsigned int seed = cfg::seed;
138#ifdef NVHLS_RAND_SEED
139 seed = (NVHLS_RAND_SEED);
140#endif
141 const char* env_rand_seed = std::getenv("NVHLS_RAND_SEED");
142 if (env_rand_seed != NULL) seed = atoi(env_rand_seed);
143 boost::random::mt19937 gen(seed);
144 boost::random::uniform_int_distribution<uint64_t> random_addr(cfg::addrBoundLower, cfg::addrBoundUpper);
145 boost::random::uniform_int_distribution<> random_wstrb(1, pow(2,WSTRB_W)-1);
146 boost::random::uniform_int_distribution<> random_burstlen(0, axiCfg::maxBurstSize-1);
147 boost::random::uniform_int_distribution<> uniform_rand;
148
149 typename axi4_::Addr wr_addr = cfg::addrBoundLower;
150 typename axi4_::Data wr_data = 0xf00dcafe12345678;
151 NVUINTW(WSTRB_W) wstrb = ~0;
152 NVUINTW(ALEN_W) wr_len = 0;
153 typename axi4_::Addr rd_addr_next;
154 typename axi4_::Addr rd_addr;
155 NVUINTW(ALEN_W) rd_len;
156 typename axi4_::AddrPayload wr_addr_pld;
157 typename axi4_::WritePayload wr_data_pld;
158 typename axi4_::WRespPayload wr_resp_pld;
159
160 done = 0;
161 unsigned int numWrites = 0;
162 unsigned int numReads = 0;
163 unsigned int numWritesOfBurst = 0;
164 unsigned int numReadsOfBurst = 0;
165 unsigned int numWriteResponses = 0;
166 unsigned int numReadResponses = 0;
167 bool writeInProgress = false;
168 bool rd_conflict = false;
169 bool wr_conflict = false;
170
171 if_rd.reset();
172 if_wr.reset();
173
174 wait(20);
175 while (1) {
176 wait();
177
178 // READ
179 if (validReadAddresses.size() > 0 && numReads < cfg::numReads) {
180 rd_addr_next = validReadAddresses[uniform_rand(gen) % validReadAddresses.size()];
181 addr_pld.addr = rd_addr_next;
182 if (axiCfg::useBurst) {
183 if (uniform_rand(gen) % 2 == 0) { // 50% of reads are bursts (less once valid addresses are disallowed)
184 rd_len = random_burstlen(gen);
185 } else {
186 rd_len = 0;
187 }
188 addr_pld.len = rd_len;
189 }
190 wr_conflict = false;
191 for (unsigned int i=0; i<(rd_len+1); i++) {
192 if (!localMem.count(rd_addr_next+bytesPerBeat*i)) {
193 wr_conflict = true; // Not actually a conflict, but the read should be cancelled nonetheless
194 }
195 }
196 std::ostringstream ms3;
197 ms3 << "\nError @" << sc_time_stamp() << " from " << name()
198 << ": Testharness attempted to read an address that it never wrote to"
199 << ", read_addr=" << hex << rd_addr_next
200 << endl;
201 BOOST_ASSERT_MSG( localMem.count(rd_addr_next), ms3.str().c_str() );
202 for (unsigned int j=0; j<waddr_queue.size(); j++) {
203 if ((rd_addr_next+bytesPerBeat*rd_len) >= waddr_queue.front() && rd_addr_next <= waddr_queue.front())
204 wr_conflict = true;
205 waddr_queue.push(waddr_queue.front());
206 waddr_queue.pop();
207 }
208 if (!wr_conflict) {
209 rd_addr_next = addr_pld.addr;
210 if (if_rd.ar.PushNB(addr_pld)) {
211 CDCOUT(sc_time_stamp() << " " << name() << " Sent read request: ["
212 << addr_pld << "]"
213 << endl, kDebugLevel);
214 numReads++;
215 for (unsigned int i=0; i<(rd_len+1); i++) {
216 raddr_queue.push(rd_addr_next);
217 rd_addr_next += bytesPerBeat;
218 }
219 rlen_queue.push(rd_len);
220 }
221 }
222 }
223 if (if_rd.r.PopNB(data_pld)) {
224 rd_addr = raddr_queue.front();
225 if (axiCfg::useWriteStrobes) {
226 bool checked_one = false;
227 for (int i=0; i<axi4_::WSTRB_WIDTH; i++) {
228 if (localMem_wstrb.count(rd_addr+i)) {
229 rcv_data_wstrb = nvhls::get_slc<8>(data_pld.data,8*i);
230 check_data_wstrb = localMem_wstrb[rd_addr+i];
231 std::ostringstream msg;
232 msg << "\nError @" << sc_time_stamp() << " from " << name()
233 << ": Incorrect read data response"
234 << ", base_addr=" << hex << rd_addr
235 << ", byte=" << i
236 << ", data=" << hex << rcv_data_wstrb
237 << ", expected=" << hex << check_data_wstrb
238 << std::endl;
239 BOOST_ASSERT_MSG( check_data_wstrb == rcv_data_wstrb, msg.str().c_str() );
240 checked_one = true;
241 }
242 }
243 std::ostringstream msg3;
244 msg3 << "\nError @" << sc_time_stamp() << " from " << name()
245 << ": Testbench did not check any bytes of a read response" << std::endl;
246 BOOST_ASSERT_MSG( checked_one, msg3.str().c_str() );
247 } else {
248 check_data = localMem[rd_addr];
249 std::ostringstream msg;
250 msg << "\nError @" << sc_time_stamp() << " from " << name()
251 << ": Incorrect read data response"
252 << ", addr=" << hex << rd_addr
253 << ", data=" << hex << data_pld.data.to_uint64()
254 << ", expected=" << hex << check_data.to_uint64()
255 << std::endl;
256 BOOST_ASSERT_MSG( check_data == data_pld.data , msg.str().c_str() );
257 }
258 std::ostringstream ms2;
259 ms2 << "\nError @" << sc_time_stamp() << " from " << name()
260 << ": Read response protocol error"
261 << ", rresp=" << data_pld.resp.to_uint64()
262 << std::endl;
263 BOOST_ASSERT_MSG( (data_pld.resp == axi4_::Enc::XRESP::OKAY) |
264 (data_pld.resp == axi4_::Enc::XRESP::EXOKAY), ms2.str().c_str() );
265 CDCOUT(sc_time_stamp() << " " << name() << " Received correct read response: ["
266 << data_pld << "]"
267 << endl, kDebugLevel);
268 raddr_queue.pop();
269 if (numReadsOfBurst++ == rlen_queue.front()) {
270 numReadsOfBurst = 0;
271 numReadResponses++;
272 rlen_queue.pop();
273 }
274 }
275
276 // WRITE
277 wr_addr_pld.addr = wr_addr;
278 wr_data_pld.data = wr_data;
279 wr_data_pld.wstrb = wstrb;
280 wr_addr_pld.len = wr_len;
281 if (!writeInProgress && numWrites < cfg::numWrites) {
282 rd_conflict = false;
283 for (unsigned int j=0; j<raddr_queue.size(); j++) {
284 if ((wr_addr+bytesPerBeat*wr_len) >= raddr_queue.front() && wr_addr <= raddr_queue.front())
285 rd_conflict = true;
286 raddr_queue.push(raddr_queue.front());
287 raddr_queue.pop();
288 }
289 if (!rd_conflict) {
290 if (if_wr.aw.PushNB(wr_addr_pld)) {
291 CDCOUT(sc_time_stamp() << " " << name() << " Sent write request: ["
292 << wr_addr_pld << "]"
293 << endl, kDebugLevel);
294 for (unsigned int i=0; i<(wr_len+1); i++) {
295 waddr_queue.push(wr_addr+bytesPerBeat*i);
296 // If the address was already written to once, it needs to be removed from the list of valid addresses
297 // because a read after this second write could return incorrect data
298 validReadAddresses.erase(std::remove(validReadAddresses.begin(), validReadAddresses.end(), waddr_queue.front()), validReadAddresses.end());
299 if (!wResp) {
300 ptrdiff_t pos = find(validReadAddresses_q.begin(), validReadAddresses_q.end(), waddr_queue.front()) - validReadAddresses_q.begin();
301 int size = validReadAddresses_q.size();
302 // If the address is already in the queue, just reset the counter
303 if (pos < size) {
304 validReadAddresses_ctr[pos] = cfg::readDelay + 20*i;
305 } else {
306 validReadAddresses_q.push_back(waddr_queue.front());
307 validReadAddresses_ctr.push_back(cfg::readDelay + 20*i);
308 }
309 waddr_queue.pop();
310 }
311 }
312 wlen_queue.push(wr_len);
313 writeInProgress = true;
314 }
315 }
316 }
317 if (writeInProgress) {
318 if (axiCfg::useBurst) {
319 if (numWritesOfBurst == (wr_len)) {
320 wr_data_pld.last = 1;
321 } else {
322 wr_data_pld.last = 0;
323 }
324 }
325 if (if_wr.w.PushNB(wr_data_pld)) {
326 CDCOUT(sc_time_stamp() << " " << name() << " Sent write data:"
327 << " addr=" << hex << wr_addr
328 << " data=[" << wr_data_pld << "]"
329 << " beat=" << dec << numWritesOfBurst
330 << endl, kDebugLevel);
331 if (axiCfg::useWriteStrobes) {
332 for (int i=0; i<axi4_::WSTRB_WIDTH; i++) {
333 if (wr_data_pld.wstrb[i] == 1) {
334 localMem_wstrb[wr_addr+i] = nvhls::get_slc<8>(wr_data_pld.data, 8*i);
335 }
336 }
337 }
338 localMem[wr_addr] = wr_data_pld.data; // Need to keep track of base addresses, even for wstrb case
339 if (++numWritesOfBurst == (wr_len+1)) { // Whole burst is done
340 wr_addr = (random_addr(gen) >> axiAddrBitsPerWord) << axiAddrBitsPerWord; // Keep all requests word-aligned
341 if (axiCfg::useBurst) {
342 if (uniform_rand(gen) % 5 == 0) { // 20% of writes are bursts
343 wr_len = random_burstlen(gen);
344 if (wr_addr + bytesPerBeat*wr_len > cfg::addrBoundUpper) wr_len = 0;
345 } else {
346 wr_len = 0;
347 }
348 }
349 writeInProgress = false;
350 numWrites++;
351 numWritesOfBurst = 0;
352 } else { // Only this beat is done
353 wr_addr += bytesPerBeat;
354 }
355 wr_data = 0xf00dcafe12345678
356 ^ ((static_cast<typename axi4_::Data>(wr_addr)) << 16) // Typically touches bits 47:16
357 ^ uniform_rand(gen); // Touches the 32 LSBs
358 wr_data.set_slc(axi4_::DATA_WIDTH-8,NVUINT8(uniform_rand(gen))); // Touches the 8 MSBs, in case of wide data words
359 if (axiCfg::useWriteStrobes) {
360 if (uniform_rand(gen) % 5 == 0) { // 20% of writes have nonuniform strobe
361 wstrb = random_wstrb(gen);
362 } else {
363 wstrb = ~0;
364 }
365 }
366 }
367 }
368 if (if_wr.b.PopNB(wr_resp_pld)) {
369 std::ostringstream msg;
370 msg << "\nError @" << sc_time_stamp() << " from " << name()
371 << ": Write response protocol error"
372 << ", bresp=" << wr_resp_pld.resp.to_uint64()
373 << ", addr=" << hex << waddr_queue.front()
374 << std::endl;
375 BOOST_ASSERT_MSG( (wr_resp_pld.resp == axi4_::Enc::XRESP::OKAY) |
376 (wr_resp_pld.resp == axi4_::Enc::XRESP::EXOKAY), msg.str().c_str() );
377 CDCOUT(sc_time_stamp() << " " << name() << " Received write response"
378 << endl, kDebugLevel);
379 numWriteResponses++;
380 for (unsigned int i=0; i<wlen_queue.front()+1; i++) {
381 validReadAddresses_q.push_back(waddr_queue.front());
382 validReadAddresses_ctr.push_back(cfg::readDelay + 20*i);
383 waddr_queue.pop();
384 }
385 wlen_queue.pop();
386 }
387 if (numWrites == cfg::numWrites &&
388 numReads == cfg::numReads &&
389 (numWriteResponses == numWrites || !wResp) &&
390 numReadResponses == numReads)
391 done = 1;
392 //DCOUT(sc_time_stamp() << " from " << name() << " numWrites: " << dec << numWrites << " numWriteResponses: " << numWriteResponses << " numReads: " << numReads << " numReadResponses: " << numReadResponses << endl);
393 std::ostringstream msg;
394 msg << "\nError @" << sc_time_stamp() << " from " << name()
395 << ": Number of write responses exceeded total number of writes"
396 << ", numWriteResponses=" << dec << numWriteResponses
397 << std::endl;
398 BOOST_ASSERT_MSG( numWriteResponses <= cfg::numWrites, msg.str().c_str() );
399 std::ostringstream ms2;
400 ms2 << "\nError @" << sc_time_stamp() << " from " << name()
401 << ": Number of read responses exceeded total number of reads"
402 << ", numReadResponses=" << dec << numReadResponses
403 << std::endl;
404 BOOST_ASSERT_MSG( numReadResponses <= cfg::numReads, msg.str().c_str() );
405
406 // If there are no write responses we still want to test reads.
407 // Finesse this by enforcing a fixed delay after a write until reads can be issued to that address.
408 for (unsigned int i=0; i<validReadAddresses_q.size(); i++) {
409 if (validReadAddresses_ctr[i] >= 0) {
410 if (validReadAddresses_ctr[i] == 0) {
411 validReadAddresses.push_back(validReadAddresses_q[i]);
412 }
413 validReadAddresses_ctr[i]--;
414 }
415 }
416 //DCOUT("validReadAddresses: ");
417 //for (typename std::vector< sc_uint<axi4_::ADDR_WIDTH> >::const_iterator q = validReadAddresses.begin(); q != validReadAddresses.end(); ++q)
418 // std::cout << *q << ' ';
419 //DCOUT(endl);
420
421 }
422 }
423};
424
425#endif
An AXI manager that generates random traffic for use in a testbench.
Definition Manager.h:87
The base axi4 class parameterized according a valid config.
Definition axi4.h:64
#define CDCOUT(x, y)
Definition hls_globals.h:73
#define NVUINTW(width)
Definition nvhls_types.h:35
nvhls_t< W >::nvuint_t get_slc(type X, const unsigned int i)
Function that returns slice of bits.
Definition nvhls_int.h:437
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
An example config for the AXI manager.
Definition Manager.h:50
Compute Celing of log2 of a constant.
Definition nvhls_int.h:174