summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlejandro Soto <alejandro@34project.org>2024-05-04 14:19:48 -0600
committerAlejandro Soto <alejandro@34project.org>2024-05-04 14:19:48 -0600
commita7d92072c0bdc3a3e1c99de64f353e932846bc2a (patch)
treecc9af86b65820c3a30cf1d0b0c97e8bfe8fc9b44
parent90cd29d85865bb5a4dbdf791616818b151881883 (diff)
rtl/pkt_switch: replace axi_timer example with pkt_switch
-rw-r--r--rtl/axi_timer/axi_timer.sv125
-rw-r--r--rtl/axi_timer/axi_timer_top.sv45
-rw-r--r--rtl/axi_timer/mod.mk10
-rw-r--r--rtl/axi_timer/testbench.py0
-rw-r--r--rtl/mod.mk2
-rw-r--r--rtl/pkt_switch/axi_bus.sv (renamed from rtl/axi_timer/axi_bus.sv)0
-rw-r--r--rtl/pkt_switch/mod.mk9
-rw-r--r--rtl/pkt_switch/pkt_switch.v136
-rw-r--r--rtl/pkt_switch/testbench.py283
9 files changed, 429 insertions, 181 deletions
diff --git a/rtl/axi_timer/axi_timer.sv b/rtl/axi_timer/axi_timer.sv
deleted file mode 100644
index 80a1805..0000000
--- a/rtl/axi_timer/axi_timer.sv
+++ /dev/null
@@ -1,125 +0,0 @@
-//axi_timer top file
-
-module axi_timer(
- input i_clk,
- input i_rst_n,
-
- axi_bus.Slave axi_slave,
-
- output logic o_IRQ
- );
-
-// verilator lint_off CASEINCOMPLETE
-// verilator lint_off WIDTHEXPAND
-// verilator lint_off WIDTHTRUNC
-
-enum logic [2:0] {
- CR_ADDR = 3'd0,
- SR_ADDR = 3'd1,
- PERIOD_ADDR = 3'd2,
- COUNTER_ADDR = 3'd3,
- IRQ_CNT_ADDR = 3'd4
-} address_map;
-
-/*Register map for axi_timer */
-//CR register
-logic enable;
-//SR register
-logic irq;
-//PERIOD register
-logic [31:0] period;
-//COUNTER register
-logic [31:0] counter;
-//IRQ_CNT register
-logic [31:0] irq_cnt;
-
-/* Native interface for access to register */
-logic addr_valid;
-logic addr;
-logic addr_ready;
-logic addr_write;
-
-logic write_valid;
-logic [31:0] wdata;
-logic write_ready;
-
-logic read_valid;
-logic [31:0] rdata;
-logic read_ready;
-
-/* Address section */
-assign addr_valid = axi_slave.AVALID;
-assign addr_write = axi_slave.AWRITE;
-assign axi_slave.AREADY = addr_ready;
-
-always_ff @ (posedge i_clk, negedge i_rst_n)
- if(!i_rst_n)
- addr_ready <= 1'b0;
- else if(addr_valid & addr_write)
- addr_ready <= 1'b1;
- else
- addr_ready <= 1'b0;
-
-always_ff @ (posedge i_clk, negedge i_rst_n)
- if(!i_rst_n)
- addr <= 'h0;
- else if(addr_ready & addr_write)
- addr <= axi_slave.ADDR;
-
-/*Write section */
-assign write_valid = axi_slave.WVALID;
-assign axi_slave.WREADY = write_ready;
-assign wdata = axi_slave.WDATA;
-
-always_ff @ (posedge i_clk, negedge i_rst_n)
- if(!i_rst_n)
- write_ready <= 1'b0;
- else if(write_valid)
- write_ready <= 1'b1;
- else
- write_ready <= 1'b0;
-
-/* registers write logic */
-always_ff @ (posedge i_clk, negedge i_rst_n)
- if(!i_rst_n) begin
- enable <= 1'b0;
- period <= 32'd0;
- irq_cnt <= 32'd0;
- end
- else if(write_valid) begin
- case(addr)
- CR_ADDR: enable <= wdata[0];
- PERIOD_ADDR: counter <= wdata;
- IRQ_CNT_ADDR: irq_cnt <= wdata;
- endcase
- end
-
-/*Read section */
-assign axi_slave.RVALID = read_valid;
-assign read_ready = axi_slave.RREADY;
-assign axi_slave.RDATA = rdata;
-
-always_ff @ (posedge i_clk, negedge i_rst_n)
- if(!i_rst_n) begin
- rdata <= 32'd0;
- read_valid <= 1'b0;
- end
- else if(read_ready) begin
- read_valid <= 1'b1;
- case(addr)
- CR_ADDR: rdata <= {31'd0, enable};
- SR_ADDR: rdata <= {31'd0, irq};
- PERIOD_ADDR: rdata <= period;
- COUNTER_ADDR: rdata <= counter;
- IRQ_CNT_ADDR: rdata <= irq_cnt;
- endcase
- end
- else begin
- read_valid <= 1'b0;
- rdata <= 32'd0;
- end
-
-
-
-
-endmodule //axi_timer
diff --git a/rtl/axi_timer/axi_timer_top.sv b/rtl/axi_timer/axi_timer_top.sv
deleted file mode 100644
index 6bd0e2a..0000000
--- a/rtl/axi_timer/axi_timer_top.sv
+++ /dev/null
@@ -1,45 +0,0 @@
-module axi_timer_top
-(
- input logic clk,
- rst_n,
-
- input logic[31:0] addr,
- input logic avalid,
- input logic awrite,
- output logic aready,
-
- input logic wvalid,
- input logic[31:0] wdata,
- output logic wready,
-
- input logic rready,
- output logic[31:0] rdata,
- output logic rvalid,
-
- output logic irq
-);
-
- axi_bus axi();
-
- assign axi.Master.ADDR = addr;
- assign axi.Master.AVALID = avalid;
- assign axi.Master.AWRITE = awrite;
- assign aready = axi.Master.AREADY;
-
- assign axi.Master.WVALID = wvalid;
- assign axi.Master.WDATA = wdata;
- assign wready = axi.Master.WREADY;
-
- assign axi.Master.RREADY = rready;
- assign rdata = axi.Master.RDATA;
- assign rvalid = axi.Master.RVALID;
-
- axi_timer timer
- (
- .i_clk(clk),
- .i_rst_n(rst_n),
- .o_IRQ(irq),
- .axi_slave(axi.Slave)
- );
-
-endmodule
diff --git a/rtl/axi_timer/mod.mk b/rtl/axi_timer/mod.mk
deleted file mode 100644
index 2efe0f4..0000000
--- a/rtl/axi_timer/mod.mk
+++ /dev/null
@@ -1,10 +0,0 @@
-define core
- $(this)/targets := sim test
-
- $(this)/rtl_top := axi_timer_top
- $(this)/rtl_dirs := .
- $(this)/rtl_files := axi_bus.sv axi_timer_top.sv
-
- $(this)/cocotb_paths := .
- $(this)/cocotb_modules := testbench
-endef
diff --git a/rtl/axi_timer/testbench.py b/rtl/axi_timer/testbench.py
deleted file mode 100644
index e69de29..0000000
--- a/rtl/axi_timer/testbench.py
+++ /dev/null
diff --git a/rtl/mod.mk b/rtl/mod.mk
index be9cb44..740118d 100644
--- a/rtl/mod.mk
+++ b/rtl/mod.mk
@@ -1,5 +1,5 @@
cores := config debounce intc
-subdirs := axi_timer cache core dma_axi32 fpu gfx perf picorv32 smp top wb2axip
+subdirs := cache core dma_axi32 fpu gfx perf picorv32 pkt_switch smp top wb2axip
define core/config
$(this)/rtl_include_dirs := .
diff --git a/rtl/axi_timer/axi_bus.sv b/rtl/pkt_switch/axi_bus.sv
index f1460ca..f1460ca 100644
--- a/rtl/axi_timer/axi_bus.sv
+++ b/rtl/pkt_switch/axi_bus.sv
diff --git a/rtl/pkt_switch/mod.mk b/rtl/pkt_switch/mod.mk
new file mode 100644
index 0000000..a76ebf0
--- /dev/null
+++ b/rtl/pkt_switch/mod.mk
@@ -0,0 +1,9 @@
+define core
+ $(this)/targets := test
+
+ $(this)/rtl_top := pkt_switch
+ $(this)/rtl_files := pkt_switch.v
+
+ $(this)/cocotb_paths := .
+ $(this)/cocotb_modules := testbench
+endef
diff --git a/rtl/pkt_switch/pkt_switch.v b/rtl/pkt_switch/pkt_switch.v
new file mode 100644
index 0000000..c146b5e
--- /dev/null
+++ b/rtl/pkt_switch/pkt_switch.v
@@ -0,0 +1,136 @@
+
+//Simple pkt switch, functionality:
+// - 1 data input interface, 2 data output interfaces
+// - data width is 8 bits, with "valid" bit
+// - packet is tranmitted continuously (valid = 1, cannot fall in the middle)
+// - when valid = 0 that means a gap between packets, packet always starts when valid 0->1
+// - first byte of the packet is address, second is packet length (in bytes), following by the data
+// - non-filtered packets are transmitted on interface 0, filtered on 1
+// - if filtering is not enabled, packets are simply forwarded on interface 0
+// - 1 control interface (write only)
+// - registers:
+// - ADDR: FUNCTIONALITY:
+// - 000 settings:
+// bit 0 - enable address-based filtering
+// bit 1 - enable length-based filtering
+// bit 2 - transmit packet on both interfaces
+// - 010 address for address-based filtering
+// - 011 address based filtering mask (which bits of the address are valid)
+// - 100 lower size limit for length based filtering
+// - 101 uppoer size limit for length based filtering
+
+module pkt_switch (
+ clk,
+ rst_n,
+ datain_data,
+ datain_valid,
+ dataout0_data,
+ dataout1_data,
+ dataout0_valid,
+ dataout1_valid,
+ ctrl_addr,
+ ctrl_data,
+ ctrl_wr
+);
+
+ input clk, rst_n;
+ //data interfaces
+ input [7:0] datain_data;
+ input datain_valid;
+ output reg [7:0] dataout0_data, dataout1_data;
+ output reg dataout0_valid, dataout1_valid;
+ //control interface
+ input [2:0] ctrl_addr;
+ input [7:0] ctrl_data;
+ input ctrl_wr;
+
+ //config registers
+ reg addr_filtering_ena_r;
+ reg len_filtering_ena_r;
+ reg transmit_both_ena_r;
+
+ reg [7:0] filter_addr_r;
+ reg [7:0] filter_addr_mask_r;
+ reg [7:0] lower_size_limit_r;
+ reg [7:0] upper_size_limit_r;
+
+ reg [7:0] data_0_r, data_1_r;
+ reg datain_valid_0_r, datain_valid_1_r;
+ reg [7:0] pkt_addr_r, pkt_len_r;
+
+ wire addr_filtering_active, channel0_active, channel1_active, len_filtering_active;
+
+ always @(posedge clk or negedge rst_n)
+ begin : config_proc
+ if(~rst_n) begin
+ addr_filtering_ena_r <= 1'b0;
+ len_filtering_ena_r <= 1'b0;
+ transmit_both_ena_r <= 1'b0;
+ filter_addr_r <= 8'd0;
+ filter_addr_mask_r <= 8'd0;
+ lower_size_limit_r <= 8'd0;
+ upper_size_limit_r <= 8'd0;
+ end else if (ctrl_wr) begin
+ case (ctrl_addr)
+ 3'b000: begin
+ addr_filtering_ena_r <= ctrl_data[0];
+ len_filtering_ena_r <= ctrl_data[1];
+ transmit_both_ena_r <= ctrl_data[2];
+ end
+ 3'b010: filter_addr_r <= ctrl_data;
+ 3'b011: filter_addr_mask_r <= ctrl_data;
+ 3'b100: lower_size_limit_r <= ctrl_data;
+ 3'b101: upper_size_limit_r <= ctrl_data;
+ default: ;
+ endcase
+ end
+ end
+
+ always @(posedge clk or negedge rst_n)
+ begin : data_proc
+ if(~rst_n) begin
+ data_0_r <= 8'd0;
+ data_1_r <= 8'd0;
+ dataout0_data <= 8'd0;
+ dataout1_data <= 8'd0;
+ datain_valid_0_r <= 1'b0;
+ datain_valid_1_r <= 1'b0;
+ end else begin
+ data_0_r <= datain_data;
+ data_1_r <= data_0_r;
+ datain_valid_0_r <= datain_valid;
+ datain_valid_1_r <= datain_valid_0_r;
+ dataout0_data <= (channel0_active) ? data_1_r : 8'd0;
+ dataout0_valid <= (channel0_active) ? datain_valid_1_r : 1'b0;
+ dataout1_data <= (channel1_active) ? data_1_r : 8'd0;
+ dataout1_valid <= (channel1_active) ? datain_valid_1_r : 1'b0;
+ end
+ end
+
+ always @(posedge clk or negedge rst_n)
+ begin : header_proc
+ if(~rst_n) begin
+ pkt_addr_r <= 8'd0;
+ pkt_len_r <= 8'd0;
+ end else begin
+ if (datain_valid && !datain_valid_0_r) //first packet byte
+ pkt_addr_r <= datain_data;
+ if (datain_valid_0_r && !datain_valid_1_r) //second packet byte
+ pkt_len_r <= datain_data;
+ end
+ end
+
+ assign addr_filtering_active =
+ addr_filtering_ena_r &&
+ ((pkt_addr_r & filter_addr_mask_r) == (filter_addr_r & filter_addr_mask_r));
+ assign len_filtering_active =
+ len_filtering_ena_r &&
+ ((pkt_len_r >= lower_size_limit_r) && (pkt_len_r <= upper_size_limit_r));
+
+ assign channel0_active =
+ !addr_filtering_active && !len_filtering_active;
+ assign channel1_active =
+ transmit_both_ena_r ||
+ addr_filtering_active || len_filtering_active;
+
+endmodule
diff --git a/rtl/pkt_switch/testbench.py b/rtl/pkt_switch/testbench.py
new file mode 100644
index 0000000..8a8b22a
--- /dev/null
+++ b/rtl/pkt_switch/testbench.py
@@ -0,0 +1,283 @@
+
+'''Copyright (c) 2020-2023, MC ASIC Design Consulting
+All rights reserved.
+
+Author: Marek Cieplucha, https://github.com/mciepluc
+
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met (The BSD 2-Clause
+License):
+
+1. Redistributions of source code must retain the above copyright notice,
+this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice,
+this list of conditions and the following disclaimer in the documentation and/or
+other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. '''
+
+"""
+Example packet switch testbench with functional coverage and constrained
+randomization. Simple packet switch is a module that routes packets from the
+input interface to output interfaces (1 or 2) depending on configured address
+or length based filter. Test generates random packets and checks if it has been
+transmitted correctly.
+"""
+
+import cocotb
+from cocotb.triggers import Timer, RisingEdge, ReadOnly
+
+from cocotb_bus.drivers import BusDriver
+from cocotb_bus.monitors import BusMonitor
+
+from cocotb_coverage.coverage import *
+from cocotb_coverage.crv import *
+
+import numpy as np
+
+class Packet(Randomized):
+ def __init__(self, data = [0, 3, 0]):
+ Randomized.__init__(self)
+ self.addr = data[0]
+ self.len = len(data)
+ self.payload = data[2:]
+
+ self.add_rand("addr", list(range(256)))
+ self.add_rand("len", list(range(3,32)))
+
+ def post_randomize(self):
+ self.payload = [np.random.randint(256) for _ in range(self.len-2)]
+
+class PacketIFDriver(BusDriver):
+ '''
+ Packet Interface Driver
+ '''
+ _signals = ["data", "valid"]
+
+ def __init__(self, entity, name, clock):
+ BusDriver.__init__(self, entity, name, clock)
+ self.clock = clock
+ self.bus.data.setimmediatevalue(0)
+ self.bus.valid.setimmediatevalue(0)
+
+ @cocotb.coroutine
+ def send(self, packet):
+ self.bus.valid.value = 1
+ # transmit header
+ self.bus.data.value = packet.addr
+ yield RisingEdge(self.clock)
+ self.bus.data.value = packet.len
+ yield RisingEdge(self.clock)
+ for byte in packet.payload:
+ self.bus.data.value = byte
+ yield RisingEdge(self.clock)
+ self.bus.valid.value = 0
+ yield RisingEdge(self.clock)
+
+class PacketIFMonitor(BusMonitor):
+ '''
+ Packet Interface Monitor
+ '''
+ _signals = ["data", "valid"]
+
+ def __init__(self, entity, name, clock):
+ BusMonitor.__init__(self, entity, name, clock)
+ self.clock = clock
+
+ @cocotb.coroutine
+ def _monitor_recv(self):
+ pkt_receiving = False
+ received_data = []
+ while True:
+ yield RisingEdge(self.clock)
+ yield ReadOnly()
+ if (self.bus.valid == 1):
+ pkt_receiving = True
+ received_data.append(int(self.bus.data))
+ elif pkt_receiving and (self.bus.valid == 0): # packet ended
+ pkt = Packet(received_data)
+ self._recv(pkt)
+ pkt_receiving = False
+ received_data = []
+
+# simple clock generator
+@cocotb.coroutine
+def clock_gen(signal, period=10000):
+ while True:
+ signal.value = 0
+ yield Timer(period/2)
+ signal.value = 1
+ yield Timer(period/2)
+
+@cocotb.test()
+async def pkt_switch_test(dut):
+ """ PKT_SWITCH Test """
+
+ log = cocotb.logging.getLogger("cocotb.test") # logger instance
+ await cocotb.start(clock_gen(dut.clk, period=100)) # start clock running
+
+ # reset & init
+ dut.rst_n.value = 1
+ dut.datain_data.value = 0
+ dut.datain_valid.value = 0
+ dut.ctrl_addr.value = 0
+ dut.ctrl_data.value = 0
+ dut.ctrl_wr.value = 0
+
+ await Timer(1000)
+ dut.rst_n.value = 0
+ await Timer(1000)
+ dut.rst_n.value = 1
+
+ # procedure of writing configuration registers
+ @cocotb.coroutine
+ def write_config(addr, data):
+ for [a, d] in zip(addr, data):
+ dut.ctrl_addr.value = a
+ dut.ctrl_data.value = d
+ dut.ctrl_wr.value = 1
+ yield RisingEdge(dut.clk)
+ dut.ctrl_wr.value = 0
+
+ enable_transmit_both = lambda: write_config([0], [4])
+ disable_filtering = lambda: write_config([0], [0])
+
+ @cocotb.coroutine
+ def enable_addr_filtering(addr, mask):
+ yield write_config([0, 2, 3], [1, addr, mask])
+
+ @cocotb.coroutine
+ def enable_len_filtering(low_limit, up_limit):
+ yield write_config([0, 4, 5], [2, low_limit, up_limit])
+
+ driver = PacketIFDriver(dut, name="datain", clock=dut.clk)
+ monitor0 = PacketIFMonitor(dut, name="dataout0", clock=dut.clk)
+ monitor1 = PacketIFMonitor(dut, name="dataout1", clock=dut.clk)
+
+ expected_data0 = [] # queue of expeced packet at interface 0
+ expected_data1 = [] # queue of expeced packet at interface 1
+
+
+ def scoreboarding(pkt, queue_expected):
+ assert pkt.addr == queue_expected[0].addr
+ assert pkt.len == queue_expected[0].len
+ assert pkt.payload == queue_expected[0].payload
+ queue_expected.pop()
+
+ monitor0.add_callback(lambda _: scoreboarding(_, expected_data0))
+ monitor1.add_callback(lambda _: scoreboarding(_, expected_data1))
+ monitor0.add_callback(lambda _: log.info("Receiving packet on interface 0 (packet not filtered)"))
+ monitor1.add_callback(lambda _: log.info("Receiving packet on interface 1 (packet filtered)"))
+
+ # functional coverage - check received packet
+
+ @CoverPoint(
+ "top.packet_length",
+ xf = lambda pkt, event, addr, mask, ll, ul: pkt.len, # packet length
+ bins = list(range(3,32)) # may be 3 ... 31 bytes
+ )
+ @CoverPoint("top.event", vname="event", bins = ["DIS", "TB", "AF", "LF"])
+ @CoverPoint(
+ "top.filt_addr",
+ xf = lambda pkt, event, addr, mask, ll, ul: # filtering based on particular bits in header
+ (addr & mask & 0x0F) if event == "AF" else None, # check only if event is "address filtering"
+ bins = list(range(16)), # check only 4 LSBs if all options tested
+ )
+ @CoverPoint(
+ "top.filt_len_eq",
+ xf = lambda pkt, event, addr, mask, ll, ul: ll == ul, # filtering of a single packet length
+ bins = [True, False]
+ )
+ @CoverPoint(
+ "top.filt_len_ll",
+ vname = "ll", # lower limit of packet length
+ bins = list(range(3,32)) # 3 ... 31
+ )
+ @CoverPoint(
+ "top.filt_len_ul",
+ vname = "ul", # upper limit of packet length
+ bins = list(range(3,32)) # 3 ... 31
+ )
+ @CoverCross(
+ "top.filt_len_ll_x_packet_length",
+ items = ["top.packet_length", "top.filt_len_ll"]
+ )
+ @CoverCross(
+ "top.filt_len_ul_x_packet_length",
+ items = ["top.packet_length", "top.filt_len_ul"]
+ )
+ def log_sequence(pkt, event, addr, mask, ll, ul):
+ log.info("Processing packet:")
+ log.info(" ADDRESS: %X", pkt.addr)
+ log.info(" LENGTH: %d", pkt.len)
+ log.info(" PAYLOAD: " + str(pkt.payload))
+ if event == "DIS":
+ log.info("Filtering disabled")
+ elif event == "TB":
+ log.info("Transmit on both interfaces")
+ elif event == "AF":
+ log.info("Address filtering, address: %02X, mask: %02X", addr, mask)
+ elif event == "LF":
+ log.info("Length filtering, lower limit: %d, upper limit: %d", ll, ul)
+
+ # main loop
+ for _ in range(1000): # is that enough repetitions to ensure coverage goal? Check out!
+
+ event = np.random.choice(["DIS", "TB", "AF", "LF"])
+ # DIS - disable filtering : expect all packets on interface 0
+ # TB - transmit bot : expect all packets on interface 0 and 1
+ # AF - address filtering : expect filtered packets on interface 1, others on 0
+ # LF - length filtering : expect filtered packets on interface 1, others on 0
+
+ # randomize test data
+ pkt = Packet();
+ pkt.randomize()
+ addr = np.random.randint(256) # 0x00 .. 0xFF
+ mask = np.random.randint(256) # 0x00 .. 0xFF
+ low_limit = np.random.randint(3,32) # 3 ... 31
+ up_limit = np.random.randint(low_limit,32) # low_limit ... 31
+
+ # expect the packet on the particular interface
+ if event == "DIS":
+ await disable_filtering()
+ expected_data0.append(pkt)
+ elif event == "TB":
+ await enable_transmit_both()
+ expected_data0.append(pkt)
+ expected_data1.append(pkt)
+ elif event == "AF":
+ await enable_addr_filtering(addr, mask)
+ if ((pkt.addr & mask) == (addr & mask)):
+ expected_data1.append(pkt)
+ else:
+ expected_data0.append(pkt)
+ elif event == "LF":
+ await enable_len_filtering(low_limit, up_limit)
+ if (low_limit <= pkt.len <= up_limit):
+ expected_data1.append(pkt)
+ else:
+ expected_data0.append(pkt)
+
+ # wait DUT
+ await driver.send(pkt)
+ await RisingEdge(dut.clk)
+ await RisingEdge(dut.clk)
+
+ # LOG the action
+ log_sequence(pkt, event, addr, mask, low_limit, up_limit)
+
+ # print coverage report
+ coverage_db.report_coverage(log.info, bins=False)
+ # export
+ coverage_db.export_to_xml(filename="coverage_pkt_switch.xml")
+ coverage_db.export_to_yaml(filename="coverage_pkt_switch.yml")