AXI muckbucket
axi_monitor.svh
Go to the documentation of this file.
1 //
3 // Copyright (C) 2017, Matt Dew @ Dew Technologies, LLC
4 //
5 // This program is free software (logic verification): you can redistribute it
6 // and/or modify it under the terms of the GNU Lesser General Public License (LGPL)
7 // as published by the Free Software Foundation, either version 3 of the License,
8 // or (at your option) any later version.
9 //
10 // This program is distributed in the hope that it will be useful, but WITHOUT
11 // ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY or
12 // FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
13 // for more details.
14 //
15 // License: LGPL, v3, as defined and found on www.gnu.org,
16 // http://www.gnu.org/licenses/lgpl.html
17 //
18 //
19 // Author's intent: If you use this AXI verification code and find or fix bugs
20 // or make improvements, then share those fixes or improvements.
21 // If you use this in a bigger project, I don't care about,
22 // or want, any changes or code outside this block.
23 // Example: If you use this in an SoC simulation/testbench
24 // I don't want, or care about, your SoC or other blocks.
25 // I just care about the enhancements to these AXI files.
26 // That's why I have choosen the LGPL instead of the GPL.
28 
41 class axi_monitor : public uvm_monitor { public:
43 
44  uvm_analysis_port <axi_seq_item> ap;
45  uvm_analysis_port <axi_seq_item> driver_activity_ap; // detect driver activity
46 
50 
51  mailbox <axi_seq_item> writedata_mbx = new(0);
52  mailbox <axi_seq_item> readdata_mbx = new(0);
53 
54 
55  new (string name="axi_monitor", uvm_component parent=null);
56 
57  void build_phase (uvm_phase phase);
58  void connect_phase (uvm_phase phase);
59  task run_phase (uvm_phase phase);
60 
61  task write_address ();
62  task write_data ();
63  task write_response ();
64  task read_address ();
65  task read_data ();
66 
67 };
68 
70  axi_monitor::new (string name="axi_monitor", uvm_component parent=null) {
71  super.new(name, parent);
72 }
73 
77  void axi_monitor::build_phase (uvm_phase phase) {
78  super.build_phase(phase);
79 
80  ap=new("ap", this);
81  driver_activity_ap=new("driver_activity_ap", this);
82 
83  vif=axi_if_abstract::type_id::create("vif", this);
84 
85 }
86 
87  void axi_monitor::connect_phase (uvm_phase phase) {
88  super.connect_phase(phase);
89 }
90 
91 
93 task axi_monitor::run_phase(uvm_phase phase) {
94  fork
95  write_address();
96  write_data();
98  read_address();
99  read_data();
100 
101  join
102 }
103 
104 
117  axi_seq_item original_item;
118  axi_seq_item item;
119  axi_seq_item cloned2_item;
120  axi_seq_item item2;
122 
123  original_item = axi_seq_item::type_id::create("original_item");
124  original_item.len=0;
125 
126 
127  forever {
128  vif.wait_for_write_address(.s(aw_s));
129  uvm_info(this.get_type_name(), "wait_for_write_address - DONE", UVM_INFO)
130 
131  $cast(item, original_item.clone());
132  axi_uvm_pkg::aw_to_class(.t(item), .v(aw_s));
133  item.cmd = axi_uvm_pkg::e_WRITE;
134 
135 
136 
137  if (m_config.drv_type == e_RESPONDER) {
138  // Sending a pkt with actual data to be put on on the read data channel.
139  // so this becomes a read data packet instead of a read (addr) packet
140  $cast(cloned2_item, item.clone());
141  //cloned2_item.cmd = axi_uvm_pkg::e_WRITE;
142  driver_activity_ap.write(cloned2_item);
143  }
144 
145 
146  // Queue up so write data channel monitor knows
147  writedata_mbx.put(item);
148  ap.write(item);
149  uvm_info("WRITE_ADDRESS", $sformatf("Item; %s", item.convert2string()), UVM_HIGH)
150 
151  // \todo sync up var name between methods
152 
153 
154  } // forever
155 }
156 
157 
171  axi_seq_item item=null;
172  axi_seq_item cloned_item=null;
173  bit <ADDR_WIDTH-1:0> write_addr;
174  int beat_cntr=0;
175  int Lower_Byte_Lane;
176  int Upper_Byte_Lane;
177  int offset;
178  string msg_s;
180 
182  return;
183  }
184 
185  forever {
186  uvm_info(this.get_type_name(),
187  "========> wait_for_write_data()",
188  UVM_HIGH)
189 
190  vif.wait_for_write_data(.s(w_s));
191  uvm_info(this.get_type_name(), "wait_for_write_data - DONE", UVM_HIGH)
192 
193  // Can we just queue the data no matter what and
194  // if the addresshasn'tarrived, we don't sit and poll continuosly
195  // for and address.
196  // What happens if we don't get an address until after wlast?
197  w_q.push_back(w_s);
198 
199  if (item == null) {
200  if (writedata_mbx.num() > 0) {
201  writedata_mbx.get(item);
202  $cast(cloned_item, item.clone());
203  cloned_item.cmd=e_WRITE_DATA;
204  cloned_item.wstrb = new[cloned_item.len];
205  cloned_item.data = new[cloned_item.len];
206 
207  beat_cntr=0;
208  }
209  }
210 
211  // if anything in data queue, write it out
212  if (item != null) {
213  while (w_q.size() > 0) {
214 
215  w_s=w_q.pop_front();
216 
218  .burst_size (item.burst_size),
219  .burst_length (item.len),
220  .burst_type (item.burst_type),
221  .beat_cnt (beat_cntr),
222  .data_bus_bytes (vif.get_data_bus_width()/8),
223  .Lower_Byte_Lane (Lower_Byte_Lane),
224  .Upper_Byte_Lane (Upper_Byte_Lane),
225  .offset (offset));
226 
227  msg_s="";
228  $sformat(msg_s, "%s beat_cntr:%0d", msg_s, beat_cntr);
229  $sformat(msg_s, "%s data_bus_bytes:%0d", msg_s, vif.get_data_bus_width()/8);
230  $sformat(msg_s, "%s Lower_Byte_Lane:%0d", msg_s, Lower_Byte_Lane);
231  $sformat(msg_s, "%s Upper_Byte_Lane:%0d", msg_s, Upper_Byte_Lane);
232  $sformat(msg_s, "%s offset:%0d", msg_s, offset);
233 
234  uvm_info("MONITOR::write_data", msg_s, UVM_HIGH)
235 
236  msg_s="wstrb: ";
237  for (int x=(vif.get_data_bus_width()/8)-1;x>=0;x--) {
238  $sformat(msg_s, "%s%0b", msg_s, w_s.wstrb[x]);
239  }
240  uvm_info("MONITOR::write_data", msg_s, UVM_HIGH)
241 
242  for (int x=Lower_Byte_Lane;x <=Upper_Byte_Lane;x++) {
243  // use get_next_address() to keep addresslogicall nice and in oneplace.
244  // Upper and Lower Wrap Boundaries are set once the write address received
245 
246 
247  write_addr=axi_pkg::get_next_address(
248  .addr(item.addr),
249  .burst_size(item.burst_size),
250  .burst_length(item.len),
251  .burst_type(item.burst_type),
252  .beat_cnt(beat_cntr),
253  .lane(x),
254  .data_bus_bytes(vif.get_data_bus_width()/8));
255 
256  if (w_s.wstrb[x] == 0b1) {
257  uvm_info("M_MEMORY.WRITE",
258  $sformatf("[0x%0x] = 0x%2x", write_addr, w_s.wdata[x*8+:8]),
259  UVM_HIGH)
260  m_memory.write(write_addr, w_s.wdata[x*8+:8]);
261  }
262 
263  }
264 
265  beat_cntr++;
266  if (w_s.wlast == 0b1) { // @Todo: count, dont rely on wlast?
267 
268  ap.write(cloned_item);
269  item=null;
270  beat_cntr=0;
271  }
272  } // while
273  }// if
274  } // forever
275 }
276 
284 
286  axi_seq_item item;
287  axi_seq_item cloned_item;
288 
289  item = axi_seq_item::type_id::create("item");
290  forever {
291  vif.wait_for_write_response(.s(b_s));
292  uvm_info(this.get_type_name(), "wait_for_write_response - DONE", UVM_HIGH)
293 
294  $cast(cloned_item, item.clone()); // Clone is faster than creating new
295  axi_uvm_pkg::b_to_class(.t(cloned_item), .v(b_s));
296  cloned_item.cmd = axi_uvm_pkg::e_WRITE_RESPONSE;
297  ap.write(cloned_item);
298 
299  } //forever
300 
301 }
302 
318  axi_seq_item item;
319  axi_seq_item cloned_item;
320  axi_seq_item cloned2_item;
321  bit <7:0> read_data;
322  bit <ADDR_WIDTH-1:0> read_addr;
323  int offset=0;
324  int doffset;
325  int beatcnt=0;
326  int beat_cnt_max;
327  int Lower_Byte_Lane;
328  int Upper_Byte_Lane;
329  string msg_s;
330  string valid_s;
331  int j;
332  int valid_asserts;
333  int valid_assert_bit;
334 
335 
337  return;
338  }
339 
340  item = axi_seq_item::type_id::create("item");
341 
342 
343  forever {
344 
345  vif.wait_for_read_address(.s(ar_s));
346 
347 
348  uvm_info(this.get_type_name(), "wait_for_read_address - DONE", UVM_HIGH)
349 
350 
351  $cast(cloned_item, item.clone());
352  axi_uvm_pkg::ar_to_class(.t(cloned_item), .v(ar_s));
353  cloned_item.cmd = axi_uvm_pkg::e_READ;
354 
355  cloned_item.data=new[cloned_item.len];
356  offset=0;
357  doffset=0;
358 
359  uvm_info("axi_monitor::read_address",
360  $sformatf("rvalid.size=%0d", m_config.rvalid.size),
361  UVM_INFO)
362 
363  valid_asserts = 0;
364  if (m_config.rvalid.size > 0) {
365  cloned_item.valid=new[m_config.rvalid.size](m_config.rvalid);
366  } else {
367  cloned_item.valid=new[cloned_item.len];
368  j=cloned_item.valid.size();
369  for (int i=0;i <j;i++) {
370  cloned_item.valid[i] = $random;
371  }
372  }
373 
374  j=cloned_item.valid.size();
375  for (int i=0;i <j;i++) {
376  if (cloned_item.valid[i] == 0b1) {
377  valid_asserts++;
378  break;
379  }
380  }
381 
382  if (valid_asserts==0) {
383  valid_assert_bit=$urandom_range(j-1,0);
384  cloned_item.valid[valid_assert_bit] = 0b1;
385  uvm_info("axi_monitor",
386  $sformatf("All zeros. Settin bit %0d to 1", valid_assert_bit),
387  UVM_INFO)
388  }
389 
390  valid_s="";
391  for (int i=0;i <j;i++) {
392  $sformat(valid_s, "%s%0b", valid_s, cloned_item.valid[i]);
393  }
394 
395 
396  beat_cnt_max=axi_pkg::calculate_axlen(.addr (cloned_item.addr),
397  .burst_size (cloned_item.burst_size),
398  .burst_length (cloned_item.len)) + 1;
399 
400  for (int beat_cntr=0;beat_cntr <beat_cnt_max;beat_cntr++) {
401 
402  axi_pkg::get_beat_N_byte_lanes(.addr (cloned_item.addr),
403  .burst_size (cloned_item.burst_size),
404  .burst_length (cloned_item.len),
405  .burst_type (cloned_item.burst_type),
406  .beat_cnt(beat_cntr),
407  .data_bus_bytes(vif.get_data_bus_width()/8),
408  .Lower_Byte_Lane(Lower_Byte_Lane),
409  .Upper_Byte_Lane(Upper_Byte_Lane),
410  .offset(offset));
411 
412  msg_s="";
413  $sformat(msg_s, "%s beat_cntr:%0d", msg_s, beat_cntr);
414  $sformat(msg_s, "%s beat_cnt_max:%0d", msg_s, beat_cnt_max);
415  $sformat(msg_s, "%s data_bus_bytes:%0d", msg_s, vif.get_data_bus_width()/8);
416  $sformat(msg_s, "%s Lower_Byte_Lane:%0d", msg_s, Lower_Byte_Lane);
417  $sformat(msg_s, "%s Upper_Byte_Lane:%0d", msg_s, Upper_Byte_Lane);
418  $sformat(msg_s, "%s offset:%0d", msg_s, offset);
419 
420 
421  uvm_info("axi_monitor::read_address", msg_s, UVM_HIGH)
422 
423 
424  for (int x=Lower_Byte_Lane;x <=Upper_Byte_Lane;x++) {
425  // use get_next_address() to keep addresslogicall nice and in oneplace.
426  // Upper and Lower Wrap Boundaries are set once the write address received
427 
428  read_addr=axi_pkg::get_next_address(
429  .addr(cloned_item.addr),
430  .burst_size(cloned_item.burst_size),
431  .burst_length(cloned_item.len),
432  .burst_type(cloned_item.burst_type),
433  .beat_cnt(beat_cntr),
434  .lane(x),
435  .data_bus_bytes(vif.get_data_bus_width()/8));
436  //if (w_s.wstrb[x] == 1'b1) begin
437  uvm_info("M_MEMORY.READ",
438  $sformatf("[0x%0x] = 0x%2x", read_addr, m_memory.read(read_addr)),
439  UVM_HIGH)
440  cloned_item.data[doffset++] = m_memory.read(read_addr);
441  //end
442  }
443 
444 
445  }
446 
447 
448  uvm_info("AR_TO_CLASS-poost", $sformatf("%s", cloned_item.convert2string()), UVM_HIGH)
449 
450 
451 
452 
453  if (m_config.drv_type == e_RESPONDER) {
454  // Sending a pkt with actual data to be put on on the read data channel.
455  // so this becomes a read data packet instead of a read (addr) packet
456  $cast(cloned2_item, cloned_item.clone());
457  cloned2_item.cmd = axi_uvm_pkg::e_READ_DATA;
458  driver_activity_ap.write(cloned2_item);
459  }
460 
461  // Now send seq item containing expected read data to slave responder
462  // If you wanna test data corruption, this seq item is an easy place to do it.
463 
464  ap.write(cloned_item);
465  readdata_mbx.put(cloned_item);
466 
467  }
468 
469 
470 }
471 
479 
481  axi_seq_item item=null;
482  axi_seq_item cloned_item=null;
483 
484  int beat_cntr=0;
485  int Lower_Byte_Lane;
486  int Upper_Byte_Lane;
487  int offset;
488  string msg_s;
490 
491  //if (m_config.drv_type != axi_uvm_pkg::e_RESPONDER) begin
492  // return;
493  //end
494 
495  forever {
496  uvm_info(this.get_type_name(),
497  "========> wait_for_read_data()",
498  UVM_HIGH)
499 
500  vif.wait_for_read_data(.s(r_s));
501  uvm_info(this.get_type_name(), "wait_for_read_data - DONE", UVM_HIGH)
502 
503  // AXI spec requires read address before read data. (otherwise how do you know
504  // what to send back?)However, we will allow it and the error will get caught
505  // and shown
506  r_q.push_back(r_s);
507 
508  if (item == null) {
509  if (readdata_mbx.num() > 0) {
510  readdata_mbx.get(item);
511  $cast(cloned_item, item.clone());
512  cloned_item.cmd=e_READ_DATA;
513  // cloned_item.initialize();
514  cloned_item.data = new[cloned_item.len];
515  }
516  }
517 
518  if (item != null) {
519  // if anything in data queue, write it out
520  while (r_q.size() > 0) {
521 
522  r_s=r_q.pop_front();
523 
524  if (r_s.rlast == 0b1) { // @Todo: count, dont rely on wlast?
525  ap.write(cloned_item);
526  cloned_item=null;
527  item=null;
528  }
529  } // while
530  } // if
531  } // forever
532 
533 
534 }
535 
rand cmd_t cmd
rand bit< 7:0 > data[]
bit< C_AXI_LEN_WIDTH-1:0 > calculate_axlen(input bit< C_AXI_ADDR_WIDTH-1:0 > addr, input bit< 2:0 > burst_size, input shortint burst_length)
calculate awlen or arlen
Definition: axi_pkg.sv:320
mailbox< axi_seq_item > readdata_mbx
Definition: axi_monitor.svh:52
rand int len
string convert2string()
Convert item&#39;s variable into one printable string.
Extremely simple memory model with just write() and read() methods.
Definition: memory.svh:32
logic< C_AXI_DATA_WIDTH/8-1:0 > wstrb
Definition: axi_pkg.sv:143
This packed struct is used to send read data channel information between the DUT and TB...
Definition: axi_pkg.sv:205
This packed struct is used to send write address channel information between the DUT and TB...
Definition: axi_pkg.sv:113
uvm_analysis_port< axi_seq_item > driver_activity_ap
Definition: axi_monitor.svh:45
virtual task wait_for_write_data(output axi_seq_item_w_vector_s s)
Wait for a valid write data to be acknowledged and return it.
logic< C_AXI_DATA_WIDTH-1:0 > wdata
Definition: axi_pkg.sv:142
task read_address()
monitors Read Address channel
rand bit wstrb[]
memory m_memory
Definition: axi_monitor.svh:49
automatic void aw_to_class(ref axi_seq_item t, input axi_seq_item_aw_vector_s v)
Pull values out of a axi_seq_item_aw_vector_s and stuffs them into an axi_seq_item.
Definition: axi_uvm_pkg.sv:125
bit< C_AXI_ADDR_WIDTH-1:0 > get_next_address(input bit< C_AXI_ADDR_WIDTH-1:0 > addr, input bit< 2:0 > burst_size, input shortint burst_length, input bit< 1:0 > burst_type, input int beat_cnt, input int lane, input int data_bus_bytes)
Get next address for reading/writing to memory.
Definition: axi_pkg.sv:447
task write_data()
monitors Write Data channel
virtual int get_data_bus_width()
returns data bus width
monitors all 5 channels for activity
Definition: axi_monitor.svh:41
rand bit< ADDR_WIDTH-1:0 > addr
virtual void write(input bit< ADDR_WIDTH-1:0 > addr, input bit< 7:0 > data)
Writes into memory.
Definition: memory.svh:58
abstract base class for polymorphic interface class (axi_if_concrete) for AXI UVM environment ...
virtual bit< 7:0 > read(input bit< ADDR_WIDTH-1:0 > addr)
Reads from memory.
Definition: memory.svh:67
rand logic< 2:0 > burst_size
automatic void b_to_class(ref axi_seq_item t, input axi_seq_item_b_vector_s v)
return values from a axi_seq_item_b_vector_s and return an axi_seq_item
Definition: axi_uvm_pkg.sv:172
virtual task wait_for_write_response(output axi_seq_item_b_vector_s s)
Wait for a valid write response to be acknowledged and return it.
void connect_phase(uvm_phase phase)
Definition: axi_monitor.svh:87
void build_phase(uvm_phase phase)
Creates the analysis port and virtual interface.
Definition: axi_monitor.svh:77
task read_data()
monitors Read Data channel and sends out TLM pkt Loop Wait for activity on the Read Data Channel...
automatic void ar_to_class(ref axi_seq_item t, input axi_seq_item_ar_vector_s v)
Pull values out of a axi_seq_item_ar_vector_s and stuffs them into an axi_seq_item.
Definition: axi_uvm_pkg.sv:229
rand bit valid[]
axi_if_abstract vif
Definition: axi_monitor.svh:47
driver_type_t drv_type
virtual task wait_for_write_address(output axi_seq_item_aw_vector_s s)
Wait for a valid write address to be acknowledged and return it.
task write_address()
monitors Write Address channel
This packed struct is used to send write data channel information between the DUT and TB...
Definition: axi_pkg.sv:141
virtual task wait_for_read_data(output axi_seq_item_r_vector_s s)
Wait for a valid read data to be acknowledged and return it.
uvm_component_utils(axi_monitor) uvm_analysis_port< axi_seq_item > ap
mailbox< axi_seq_item > writedata_mbx
Definition: axi_monitor.svh:51
void get_beat_N_byte_lanes(input bit< C_AXI_ADDR_WIDTH-1:0 > addr, input bit< 2:0 > burst_size, input shortint burst_length, input bit< 1:0 > burst_type, input int beat_cnt, input int data_bus_bytes, output int Lower_Byte_Lane, output int Upper_Byte_Lane, output int offset)
return byte lanes that contain valid data
Definition: axi_pkg.sv:538
task write_response()
monitors Write Response channel and sends out TLM pkt Loop Wait for activity on the Write Response Ch...
virtual task wait_for_read_address(output axi_seq_item_ar_vector_s s)
Wait for a valid read address to be acknowledged and return it.
new(string name="axi_monitor", uvm_component parent=null)
Constructor.
Definition: axi_monitor.svh:70
axi_agent_config m_config
Definition: axi_monitor.svh:48
This packed struct is used to send write response channel information between the DUT and TB...
Definition: axi_pkg.sv:162
Configuration object for an axi_agent.
rand logic< 1:0 > burst_type
contains all data and functions related to axi and usage
This packed struct is used to send read address channel information between the DUT and TB...
Definition: axi_pkg.sv:178
task run_phase(uvm_phase phase)
Starts the monitoring threads.
Definition: axi_monitor.svh:93
localparam ADDR_WIDTH
Definition: axi_uvm_pkg.sv:39