16 #ifndef __AXI_T_MANAGER__
17 #define __AXI_T_MANAGER__
20 #include <ac_reset_signal_is.h>
23 #include <nvhls_connections.h>
24 #include <hls_globals.h>
32 #include <boost/assert.hpp>
34 #include <boost/random/mersenne_twister.hpp>
35 #include <boost/random/uniform_int_distribution.hpp>
58 static const uint64_t addrBoundLower = 0;
59 static const uint64_t addrBoundUpper = 0xFFFFFFFF;
86 template <
typename axiCfg,
typename cfg>
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");
91 static const int kDebugLevel = 1;
94 typename axi4_::read::template manager<> if_rd;
95 typename axi4_::write::template manager<> if_wr;
97 sc_in<bool> reset_bar;
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;
106 static const int bytesPerBeat = axi4_::DATA_WIDTH >> 3;
107 static const bool wResp = axiCfg::useWriteResponses;
112 : if_rd(
"if_rd"), if_wr(
"if_wr"), reset_bar(
"reset_bar"), clk(
"clk") {
115 sensitive << clk.pos();
116 async_reset_signal_is(reset_bar,
false);
121 static const int bytesPerWord = axi4_::DATA_WIDTH >> 3;
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;
137 unsigned int seed = cfg::seed;
138 #ifdef NVHLS_RAND_SEED
139 seed = (NVHLS_RAND_SEED);
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;
149 typename axi4_::Addr wr_addr = cfg::addrBoundLower;
150 typename axi4_::Data wr_data = 0xf00dcafe12345678;
153 typename axi4_::Addr rd_addr_next;
154 typename axi4_::Addr rd_addr;
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;
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) {
184 rd_len = random_burstlen(gen);
188 addr_pld.len = rd_len;
191 for (
unsigned int i=0; i<(rd_len+1); i++) {
192 if (!localMem.count(rd_addr_next+bytesPerBeat*i)) {
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
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())
205 waddr_queue.push(waddr_queue.front());
209 rd_addr_next = addr_pld.addr;
210 if (if_rd.ar.PushNB(addr_pld)) {
211 CDCOUT(sc_time_stamp() <<
" " << name() <<
" Sent read request: ["
213 << endl, kDebugLevel);
215 for (
unsigned int i=0; i<(rd_len+1); i++) {
216 raddr_queue.push(rd_addr_next);
217 rd_addr_next += bytesPerBeat;
219 rlen_queue.push(rd_len);
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
236 <<
", data=" << hex << rcv_data_wstrb
237 <<
", expected=" << hex << check_data_wstrb
239 BOOST_ASSERT_MSG( check_data_wstrb == rcv_data_wstrb, msg.str().c_str() );
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() );
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()
256 BOOST_ASSERT_MSG( check_data == data_pld.data , msg.str().c_str() );
258 std::ostringstream ms2;
259 ms2 <<
"\nError @" << sc_time_stamp() <<
" from " << name()
260 <<
": Read response protocol error"
261 <<
", rresp=" << data_pld.resp.to_uint64()
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: ["
267 << endl, kDebugLevel);
269 if (numReadsOfBurst++ == rlen_queue.front()) {
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) {
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())
286 raddr_queue.push(raddr_queue.front());
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);
298 validReadAddresses.erase(std::remove(validReadAddresses.begin(), validReadAddresses.end(), waddr_queue.front()), validReadAddresses.end());
300 ptrdiff_t pos = find(validReadAddresses_q.begin(), validReadAddresses_q.end(), waddr_queue.front()) - validReadAddresses_q.begin();
301 int size = validReadAddresses_q.size();
304 validReadAddresses_ctr[pos] = cfg::readDelay + 20*i;
306 validReadAddresses_q.push_back(waddr_queue.front());
307 validReadAddresses_ctr.push_back(cfg::readDelay + 20*i);
312 wlen_queue.push(wr_len);
313 writeInProgress =
true;
317 if (writeInProgress) {
318 if (axiCfg::useBurst) {
319 if (numWritesOfBurst == (wr_len)) {
320 wr_data_pld.last = 1;
322 wr_data_pld.last = 0;
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);
338 localMem[wr_addr] = wr_data_pld.data;
339 if (++numWritesOfBurst == (wr_len+1)) {
340 wr_addr = (random_addr(gen) >> axiAddrBitsPerWord) << axiAddrBitsPerWord;
341 if (axiCfg::useBurst) {
342 if (uniform_rand(gen) % 5 == 0) {
343 wr_len = random_burstlen(gen);
344 if (wr_addr + bytesPerBeat*wr_len > cfg::addrBoundUpper) wr_len = 0;
349 writeInProgress =
false;
351 numWritesOfBurst = 0;
353 wr_addr += bytesPerBeat;
355 wr_data = 0xf00dcafe12345678
356 ^ ((
static_cast<typename axi4_::Data
>(wr_addr)) << 16)
358 wr_data.set_slc(axi4_::DATA_WIDTH-8,NVUINT8(uniform_rand(gen)));
359 if (axiCfg::useWriteStrobes) {
360 if (uniform_rand(gen) % 5 == 0) {
361 wstrb = random_wstrb(gen);
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()
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);
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);
387 if (numWrites == cfg::numWrites &&
388 numReads == cfg::numReads &&
389 (numWriteResponses == numWrites || !wResp) &&
390 numReadResponses == numReads)
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
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
404 BOOST_ASSERT_MSG( numReadResponses <= cfg::numReads, msg.str().c_str() );
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]);
413 validReadAddresses_ctr[i]--;
An AXI manager that generates random traffic for use in a testbench.
The base axi4 class parameterized according a valid config.
NVUINTW(Wrapped< T >::width) TypeToNVUINT(T in)
Convert Type to NVUINT.
A struct composed of the signals associated with AXI read and write requests.
A struct composed of the signals associated with an AXI read response.
A struct composed of the signals associated with an AXI write response.
A struct composed of the signals associated with AXI write data.
An example config for the AXI manager.
Compute Celing of log2 of a constant.