def apply_input(dut, meta, data): """Apply meta data and packet data as DUT stimulus. Meta data is written to the input meta data FIFO, packet data is written to the input packet data FIFO. """ # data word index i = 0 # iterate over meta data words for m in meta: # get the capture length len_capture = (m >> 64) & 0x7FF # calculate the number of 64 bit data words if len_capture % 8 == 0: len_capture_words = len_capture / 8 else: len_capture_words = len_capture / 8 + 1 # apply data words at DUT input for _ in range(len_capture_words): # get data word and increment index d = data[i] i += 1 # make sure input data fifo is not full check_value("fifo_data_full_i", dut.fifo_data_full_o, 0x0) # apply data word dut.fifo_data_din_i <= d dut.fifo_data_wr_en_i <= 1 yield RisingEdge(dut.clk) # done writing data dut.fifo_data_wr_en_i <= 0 # wait a random number of cycles until applying the meta data word yield wait_n_cycles(dut.clk, random.randint(0, 10)) # make sure input meta data fifo is not full check_value("fifo_meta_full_o", dut.fifo_meta_full_o, 0x0) # apply meta data word dut.fifo_meta_din_i <= m dut.fifo_meta_wr_en_i <= 1 yield RisingEdge(dut.clk) # done applying meta data dut.fifo_meta_wr_en_i <= 0 # wait a random number of cycles again yield wait_n_cycles(dut.clk, random.randint(0, 10))
def perform_test(dut, axis_writer, pkts, latencies, inter_packet_times, active, max_len_capture): """Perform a test run for specific capture parameters.""" # enable/disable capture dut.active_i <= active # set per-packet capture length dut.max_len_capture_i <= max_len_capture # wait a few cycles yield wait_n_cycles(dut.clk, 5) # if capture is enabled, module shall be active now. otherwise it should # remain deactivated check_value("active_o", dut.active_o, active) # start writing packets to DuT input coroutine_pkts_write = cocotb.fork(packets_write(dut, axis_writer, pkts, latencies, inter_packet_times)) # start coroutine that checks module output coroutine_check_output = cocotb.fork(check_output(dut, pkts, latencies, inter_packet_times)) # wait for coroutines to complete yield coroutine_pkts_write.join() yield coroutine_check_output.join() # disable module dut.active_i <= 0 # wait a few cycles yield wait_n_cycles(dut.clk, 3) # make sure module is now inactive check_value("active_o", dut.active_o, 0x0)
def check_output(dut, trace): """Check data written to the output FIFO for correctness. The coroutine monitors the data written to the FIFO and checks whether it matches the data of the input trace file. """ # get trace size trace_size = trace.size() # check data written to fifo for correctness for i in range(trace_size * 8 / AXI_BIT_WIDTH): # wait for fifo wr enable while True: yield RisingEdge(dut.clk) # make sure module active status signal is high check_value("status_active_o", int(dut.status_active_o), 1) if int(dut.fifo_wr_en_o): # output fifo data is valid break # the order of the 8 byte words is reversed in the 64 byte output word output = (int(dut.fifo_din_o) & 2**64 - 1) << 448 output |= ((int(dut.fifo_din_o) >> 64) & 2**64 - 1) << 384 output |= ((int(dut.fifo_din_o) >> 128) & 2**64 - 1) << 320 output |= ((int(dut.fifo_din_o) >> 192) & 2**64 - 1) << 256 output |= ((int(dut.fifo_din_o) >> 256) & 2**64 - 1) << 192 output |= ((int(dut.fifo_din_o) >> 320) & 2**64 - 1) << 128 output |= ((int(dut.fifo_din_o) >> 384) & 2**64 - 1) << 64 output |= (int(dut.fifo_din_o) >> 448) & 2**64 - 1 # get exepcted output output_ref = trace.read_reverse_byte_order(i * AXI_BIT_WIDTH / 8, AXI_BIT_WIDTH / 8) # make sure values match check_value("fifo_din_o", output_ref, output) # wait one clock cycle and make sure active signal is low then yield RisingEdge(dut.clk) check_value("status_active_o", int(dut.status_active_o), 0)
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 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)