def packets_read(dut, axis_reader, pkts_ref, check_timing): """Check DuT output for correctness.""" p = None for i, (pkt_ref, inter_packet_cycles_ref) in enumerate(pkts_ref): # read AXI4-Stream data (tdata, tkeep, _) = yield axis_reader.read() # convert AXI4-Stream data to scapy packet pkt = axis_data_to_packet(tdata, tkeep, AXIS_BIT_WIDTH) # make sure packets match if str(pkt) != str(pkt_ref): raise cocotb.result.TestFailure("Packet #%d: wrong data" % i) # get cycles since between transmissions cycles_transmission = cntr_cycles_between_axis_transmission if check_timing and i > 0 \ and cntr_cycles_between_axis_transmission != p: raise cocotb.result.TestFailure( ("Packet #%d: wrong timing, " + "Cycles IS: %d " + "Cycles Expected %d ") % (i, cycles_transmission, p)) p = inter_packet_cycles_ref # print progress print_progress(i, N_PACKETS)
def packets_read(dut, axis_reader, pkts_ref, timestamps): """Evaluate data at DuT output and validate correct behavior.""" # read as many packets as we originally generated for i, pkt_ref in enumerate(pkts_ref): # read axi stream data (tdata, tkeep, _) = yield axis_reader.read() # convert axi stream data to scapy packet pkt = axis_data_to_packet(tdata, tkeep, AXIS_BIT_WIDTH) # check if sent and received packets match and if (possibly) inserted # timestamps match if int(dut.mode_i) == MODE_DISABLED: # timestamping is disabled, received and sent packets must be # identical valid = str(pkt) == str(pkt_ref) elif int(dut.mode_i) == MODE_HEADER: # timestamp is located in packet header valid = validate_packet_timestamp_header(pkt_ref, pkt, timestamps[i]) elif int(dut.mode_i) == MODE_FIXED_POS: # timestamp is loacted at fixed byte position valid = validate_packet_timestamp_fixed(dut, pkt_ref, pkt, timestamps[i]) else: # this should never happen assert False if not valid: raise cocotb.result.TestFailure( ("Packet #%d: received invalid " + "packet data or timestamp") % i) # print progress print_progress(i, N_PACKETS)
def packets_read(dut, axis_reader, pkts_ref): """Evaluate correct packet data at DuT output.""" # we must read as many packets as we appyed at the input for i, pkt_ref in enumerate(pkts_ref): # read AXI4-Stream data (tdata, tkeep, tuser) = yield axis_reader.read() # convert AXI4-Stream data to scapy packet pkt = axis_data_to_packet(tdata, tkeep, AXIS_BIT_WIDTH) # make sure read packet matches the one we expected if str(pkt) != str(pkt_ref): raise cocotb.result.TestFailure(("Packet #%d: read invalid " + "packet data") % i) # make sure that all TUSER values except the last one are set to zero if any(v != 0 for v in tuser[1:len(tuser)-1]): raise cocotb.result.TestFailure(("Packet #%d: invalid TUSER " + "value (!= 0)") % i) # the 25 LSB of the last TUSER value must match the value we applied on # the input if tuser[-1] & 0x1FFFFFF != TUSER_LSB: raise cocotb.result.TestFailure(("Packet #%d: invalid TUSER " + "value (!= input)") % i) # print progress print_progress(i, N_PACKETS) # inter-packet time is a relative number. start evaluation with second # packet if i == 0: continue # extract inter-packet time from TUSER inter_packet_cycles = tuser[len(tuser)-1] >> 25 # get the expected inter-packet time from FIFO inter_packet_cycles_ref = inter_packet_cycles_monitor.pop(0) # make sure the extracted inter-packet time matches the one monitored # on the DuT output if inter_packet_cycles != inter_packet_cycles_ref: raise cocotb.result.TestFailure(("Packet #%d: invalid " + "inter-packet time " + "(Expected: %d, Is: %d)") % (i, inter_packet_cycles_ref, inter_packet_cycles))
def frames_read(dut, axis_reader, frames_ref): """Evaluate data at DuT output and validates correct behavior.""" # iterate over the list of frames that we are expecting to read for i, frame_ref in enumerate(frames_ref): # read AXI4-Stream data (tdata, tkeep, _) = yield axis_reader.read() # convert AXI4-Stream data to scapy frame frame = axis_data_to_packet(tdata, tkeep, AXIS_BIT_WIDTH) # check if written and read frames are equal if str(frame) != str(frame_ref): raise cocotb.result.TestFailure("Frame #%d: invalid data" % i) # print progress print_progress(i, N_FRAMES)
def check_output(dut, trace, axis_reader): """Check whether the DUT output is the one that is expected. Based on a given trace replay file, the coroutine constructs the expected output behavior of the DUT and compares it to the actual values. """ # get trace size trace_size = trace.size() # initialize address used to index memory-mapped trace file addr = 0 while addr < trace_size: # read 8 byte from trace file. contains packet meta data meta = trace.read_reverse_byte_order(addr, 8) addr += 8 if meta == 2**64 - 1: # the overall trace data has to be 512 bit aligned. If the actual # trace size is smaller, we can add padding at the end of the # trace (in multiples of 64 bit words). all bits of the padding # data have to be set to 1 continue # extract meta data meta_delta_t = meta & 2**32 - 1 meta_len_snap = (meta >> 32) & 2**11 - 1 meta_len_wire = (meta >> 48) & 2**11 - 1 # read packet data from trace file data = trace.read(addr, meta_len_snap) # increase address. packet data is aligned to 8 byte aligned if meta_len_snap % 8 == 0: addr += meta_len_snap else: addr += 8 * (meta_len_snap / 8 + 1) # if number of bytes on the wire is larger than the number of snap # bytes, add zero bytes as padding for _ in range(meta_len_wire - meta_len_snap): data <<= 8 # create reference ethernet frame from the read data data = "%x" % data data = data.zfill(meta_len_wire) frame_ref = Ether(binascii.unhexlify(data)) # read arriving frame from AXI4-Stream (tdata, tkeep, tuser) = yield axis_reader.read() # convert AXI4-Stream data to ethernet frame frame_recv = axis_data_to_packet(tdata, tkeep, AXIS_BIT_WIDTH) # make sure frames match if str(frame_ref) != str(frame_recv): raise cocotb.result.TestFailure("received wrong data") # inter-packet time is located in first tuser word meta_delta_t_recv = tuser[0] & 2**32 - 1 # make sure the inter-packet time matches the expected one if meta_delta_t != meta_delta_t_recv: raise cocotb.result.TestFailure("wrong timing information") # all other tuser fields must be set to zero if any(v != 0 for v in tuser[2:]): raise cocotb.result.TestFailure("invalid tuser data") # wait some more cycles after last packet. there should not be any data on # the axi stream anymore for _ in range(1000): yield RisingEdge(dut.clk) check_value("m_axis_tvalid", dut.m_axis_tvalid, 0)
def packets_read(dut, axis_reader, pkts_ref, timestamps_ref): """Evaluate data at DuT output and validate correct behavior.""" # read as many packets as we originally generated for i, pkt_ref in enumerate(pkts_ref): # read AXI4-Stream data (tdata, tkeep, tuser) = yield axis_reader.read() # print progress print_progress(i, N_PACKETS) # convert AXI4-Stream data to scapy packet pkt = axis_data_to_packet(tdata, tkeep, AXIS_BIT_WIDTH) # make sure received packet matches expected packet if str(pkt) != str(pkt_ref): raise cocotb.result.TestFailure( ("Packet #%d: received invalid " + "packet data") % i) # make sure that all tuser values except the last one are set to zero if any(v != 0 for v in tuser[1:-1]): raise cocotb.result.TestFailure("Packet #%d: invalid TUSER value" % i) if int(dut.mode_i) == MODE_HEADER: # latency timestamp is saved in IP packet header. latency values # must be extracted for all IP packets if pkt_ref.type == 0x800 or pkt_ref.type == 0x86dd: # latency value provided at output? latency_valid = tuser[-1] >> 24 if latency_valid == 0: raise cocotb.result.TestFailure( ("Packet #%d: no " + "latency value in " + "output") % i) # get latency value latency = tuser[-1] & 0xFFFFFF # calculate reference latency timestamp_cur = int(dut.timestamp_i) & 0xFFFF if timestamp_cur > timestamps_ref[i]: latency_ref = timestamp_cur - timestamps_ref[i] else: latency_ref = 0xFFFF - timestamps_ref[i] + \ timestamp_cur + 1 # make sure latency values match if latency != latency_ref: raise cocotb.result.TestFailure( ("Packet #%d: wrong " + "latency value") % i) else: # non latency value must be provided on output for non-IP # packets if tuser[-1] != 0: raise cocotb.result.TestFailure( ("Packet #%d: latency " + "value on output") % i) elif int(dut.mode_i) == MODE_FIXED_POS: # latency timestamp is located at a fixed byte position in the # packet # is packet long enough to contain a timestamp? if int(dut.width_i) == 0: # timestamp is 16 bit wide if len(pkt_ref) < int(dut.pos_i) + 2: # not long enough, make sure that no latency value is # provided if tuser[-1] != 0: raise cocotb.result.TestFailure( ("Packet #%d: " + "latency value on " + "output") % i) # ensure that we did not generate a timestamp for this # packet assert timestamps_ref[i] is None # nothing more to do for this packet continue else: # timestamp is 24 bit wide if len(pkt_ref) < int(dut.pos_i) + 3: # not long enough, make sure that no latency value is # provided if tuser[-1] != 0: raise cocotb.result.TestFailure( ("Packet #%d: " + "latency value on " + "output") % i) # ensure that we did not generate a timestamp for this # packet assert timestamps_ref[i] is None # nothing more to do for this packet continue # make sure latency value is provided at output latency_valid = tuser[-1] >> 24 if latency_valid == 0: raise cocotb.result.TestFailure( ("Packet #%d: no " + "latency value in " + "output") % i) # get latency value latency = tuser[-1] & 0xFFFFFF # calculate reference latency if int(dut.width_i) == 0: # 16 bit timestamp timestamp_cur = int(dut.timestamp_i) & 0xFFFF if timestamp_cur > timestamps_ref[i]: latency_ref = timestamp_cur - timestamps_ref[i] else: latency_ref = 0xFFFF - timestamps_ref[i] + \ timestamp_cur + 1 else: # 24 bit timestamp timestamp_cur = int(dut.timestamp_i) if timestamp_cur > timestamps_ref[i]: latency_ref = timestamp_cur - timestamps_ref[i] else: latency_ref = 0xFFFFFF - timestamps_ref[i] + \ timestamp_cur + 1 # make sure latency values match if latency != latency_ref: raise cocotb.result.TestFailure( ("Packet #%d: wrong " + "latency value") % i) elif int(dut.mode_i) == MODE_DISABLED: # timestamping is disabled # all bits of the last tuser value must be set to zero if tuser[-1] != 0: raise cocotb.result.TestFailure(("Packet #%d: latency value " "in output") % i) else: # this should never happen assert False
def check_data(pkts_ref, latencies_ref, inter_packet_times_ref, data, max_len_capture): """Check the received data for correctness. The function ensures that the data read from the ring buffer (a list of 512 bit data words) matches the expected meta data (timestamps, wire + capture length) and packet data. """ # data word index i_data = 0 # iterate over all packets for i_pkt, pkt_ref in enumerate(pkts_ref): # determinal actual capture length len_capture = min(len(pkt_ref), max_len_capture) # data is captured at the granularity of 8 byte words. how many 8 byte # words do we have? if len_capture % 8 == 0: len_capture_words = len_capture / 8 else: len_capture_words = len_capture / 8 + 1 # initialize empty packet data list packet_data = [] # iterate over captured data words (8 byte each) for i in range(len_capture_words + 1): # get data word and increment data word index d = data[i_data] i_data += 1 # swap byte order d = swp_byte_order(d, AXIS_BIT_WIDTH / 8) if i == 0: # this is meta data meta_latency = d & 0xFFFFFF meta_latency_valid = (d >> 24) & 0x1 meta_interpackettime = (d >> 25) & 0xFFFFFFF meta_len_wire = (d >> 53) & 0x7FF # make sure the latency is marked valid if meta_latency_valid != 0x1: raise cocotb.result.TestFailure( ("Packet #%d: " + "Latency value not " + "valid") % i) # make sure latency matches reference value if latencies_ref[i_pkt] != meta_latency: raise cocotb.result.TestFailure( ("Packet #%d: " + "incorrect latency") % i_pkt) # make sure inter-packet time matches reference value if inter_packet_times_ref[i_pkt] != meta_interpackettime: raise cocotb.result.TestFailure( ("Packet #%d: " + "incorrect inter-" + "packet time") % i_pkt) # make sure wire length matches packet length if len(pkt_ref) != meta_len_wire: raise cocotb.result.TestFailure( ("Packet #%d: " + "invalid wire " + "length") % i_pkt) else: # this is packet data packet_data.append(d) # create packet from captured data if len_capture % 8 == 0: pkt = axis_data_to_packet(packet_data, 2**8 - 1, 64) else: pkt = axis_data_to_packet(packet_data, 2**(len_capture % 8) - 1, 64) # make sure packet data matches the exepcted packet data if str(pkt)[0:len_capture] != \ str(pkt_ref)[0:len_capture]: raise cocotb.result.TestFailure( ("Packet #%d: " + "invalid data") % i_pkt)
def check_output(dut, pkts_ref, latencies_ref, inter_packet_times_ref): """Check DuT output for correctness.""" # check whether capturing is enabled enabled = int(dut.active_i) == 1 if not enabled: # packet data capturing is disabled. make sure that the module does not # output any data pkt_cnt = 0 while pkt_cnt < len(pkts_ref): # the module should not write any data to the FIFOs at all yield RisingEdge(dut.clk) assert int(dut.fifo_meta_wr_en_o) == 0 assert int(dut.fifo_data_wr_en_o) == 0 assert int(dut.pkt_cnt_o) == 0 if int(dut.s_axis_tvalid) and int(dut.s_axis_tready) and \ int(dut.s_axis_tlast): # one full packet has been applied at DuT input -> print # progress and increment packet counter print_progress(pkt_cnt, N_PACKETS) pkt_cnt += 1 # make sure packet counter is still at zero check_value("pkt_cnt_o", dut.pkt_cnt_o, 0x0) else: # packet capture is enabled # get the configured maximum capture length max_len_capture = int(dut.max_len_capture_i) data = [] # iterate over all reference packets for i, pkt_ref in enumerate(pkts_ref): # calculate the expected capture length meta_len_capture_ref = min(len(pkt_ref), max_len_capture) # each data FIFO word is 8 bytes wide, calculate number of data # words that shall be written for the captured data if meta_len_capture_ref % 8 == 0: n_words_ref = meta_len_capture_ref / 8 else: n_words_ref = (meta_len_capture_ref / 8) + 1 while True: yield RisingEdge(dut.clk) if int(dut.fifo_meta_wr_en_o): # meta data is written to FIFO in this cycle # get meta data meta_data = int(dut.fifo_meta_din_o) # make sure that the correct number of data words have been # written to the FIFO assert len(data) == n_words_ref # extract meta data meta_latency = meta_data & 0xFFFFFF meta_latency_valid = (meta_data >> 24) & 0x1 meta_interpackettime = (meta_data >> 25) & 0xFFFFFFF meta_len_wire = (meta_data >> 53) & 0x7FF meta_len_capture = (meta_data >> 64) & 0x7FF # make sure the latency is marked valid if meta_latency_valid != 0x1: raise cocotb.result.TestFailure(("Packet #%d: " + "Latency value not " + "valid") % i) # make sure latency matches reference value if latencies_ref[i] != meta_latency: raise cocotb.result.TestFailure(("Packet #%d: " + "incorrect latency") % i) # make sure inter-packet time matches reference value if inter_packet_times_ref[i] != meta_interpackettime: raise cocotb.result.TestFailure(("Packet #%d: " + "incorrect inter-" + "packet time") % i) # make sure wire length matches packet length if len(pkt_ref) != meta_len_wire: raise cocotb.result.TestFailure(("Packet #%d: " + "invalid wire " + "length") % i) # make sure capture length matches expected value if meta_len_capture != meta_len_capture_ref: raise cocotb.result.TestFailure(("Packet #%d: " + "invalid capture" + "length") % i) # create packet from captured data if meta_len_capture % (DATAPATH_BIT_WIDTH/8) == 0: pkt = axis_data_to_packet(data, 2**(DATAPATH_BIT_WIDTH/8)-1, DATAPATH_BIT_WIDTH) else: pkt = axis_data_to_packet(data, 2**(meta_len_capture % 8)-1, DATAPATH_BIT_WIDTH) # make sure packet data matches the exepcted packet data if str(pkt)[0:meta_len_capture] != \ str(pkt_ref)[0:meta_len_capture]: raise cocotb.result.TestFailure(("Packet #%d: " + "invalid data") % i) # delete captured data data = [] if int(dut.fifo_data_wr_en_o): # data is being written to the data FIFO data.append(int(dut.fifo_data_din_o)) if int(dut.fifo_meta_wr_en_o): # meta data has been written and checked -> we are done for # this packet. print_progress(i, N_PACKETS) break # make sure packet counter value is correct check_value("pkt_cnt_o", dut.pkt_cnt_o, len(pkts_ref)) # make sure no errors are flagged by the DuT check_value("err_data_fifo_full_o", dut.err_data_fifo_full_o, 0x0) check_value("err_meta_fifo_full_o", dut.err_meta_fifo_full_o, 0x0)
def check_output(dut, pkts_ref, latencies_ref, inter_packet_times_ref): """Check DuT output for correctness.""" # check whether capturing is enabled enabled = int(dut.active_i) == 1 if not enabled: # packet data capturing is disabled. make sure that the module does not # output any data pkt_cnt = 0 while pkt_cnt < len(pkts_ref): # the module should not write any data to the output FIFO at all yield RisingEdge(dut.clk) assert int(dut.fifo_wr_en_o) == 0 assert int(dut.pkt_cnt_o) == 0 if int(dut.s_axis_tvalid) and int(dut.s_axis_tready) and \ int(dut.s_axis_tlast): # one full packet has been applied at DuT input -> print # progress and increment packet counter print_progress(pkt_cnt, N_PACKETS) pkt_cnt += 1 if pkt_cnt == len(pkts_ref): # all packets have been applied -> done! break # make sure packet counter is still at zero check_value("pkt_cnt_o", dut.pkt_cnt_o, 0x0) else: # packet capture is enabled # get the configured maximum capture length max_len_capture = int(dut.max_len_capture_i) # iterate over all reference packets for i, pkt_ref in enumerate(pkts_ref): # get the packet's capture length len_capture = min(len(pkt_ref), max_len_capture) # wait for fifo write output signal to become high while True: yield RisingEdge(dut.clk) if int(dut.fifo_wr_en_o): break # get meta data meta_data = int(dut.fifo_din_o) # extract meta data meta_latency = meta_data & 0xFFFFFF meta_latency_valid = (meta_data >> 24) & 0x1 meta_interpackettime = (meta_data >> 25) & 0xFFFFFFF meta_len_wire = (meta_data >> 53) & 0x7FF # make sure the latency is marked valid if meta_latency_valid != 0x1: raise cocotb.result.TestFailure( ("Packet #%d: " + "Latency value not " + "valid") % i) if latencies_ref[i] != meta_latency: raise cocotb.result.TestFailure( ("Packet #%d: " + "incorrect latency") % i) # make sure inter-packet time matches reference value if inter_packet_times_ref[i] != meta_interpackettime: raise cocotb.result.TestFailure( ("Packet #%d: " + "incorrect inter-" + "packet time") % i) # make sure wire length matches packet length if len(pkt_ref) != meta_len_wire: raise cocotb.result.TestFailure( ("Packet #%d: " + "invalid wire " + "length") % i) # calculate number of 8 byte packet data words if len_capture % 8 == 0: len_capture_words = len_capture / 8 else: len_capture_words = len_capture / 8 + 1 # read as many data words as specified by capture length data = [] for _ in range(len_capture_words): # wait for FIFO write output signal to become high while True: yield RisingEdge(dut.clk) if int(dut.fifo_wr_en_o): break # read data word data.append(int(dut.fifo_din_o)) # create packet from captured data if len_capture % (DATAPATH_BIT_WIDTH / 8) == 0: pkt = axis_data_to_packet(data, 2**(DATAPATH_BIT_WIDTH / 8) - 1, DATAPATH_BIT_WIDTH) else: pkt = axis_data_to_packet(data, 2**(len_capture % 8) - 1, DATAPATH_BIT_WIDTH) # make sure packet data matches the exepcted packet data if str(pkt)[0:len_capture] != \ str(pkt_ref)[0:len_capture]: raise cocotb.result.TestFailure( ("Packet #%d:" + "invalid data") % i) # print progress print_progress(i, N_PACKETS) # make sure packet counter value is correct check_value("pkt_cnt_o", dut.pkt_cnt_o, len(pkts_ref)) # make sure no errors are flagged by the DuT check_value("err_data_fifo_full_o", dut.err_data_fifo_full_o, 0x0) check_value("err_meta_fifo_full_o", dut.err_meta_fifo_full_o, 0x0)