mirror of
https://github.com/corundum/corundum.git
synced 2025-01-16 08:12:53 +08:00
Add cocotb MAC testbenches
This commit is contained in:
parent
0359d8d76a
commit
29dc7498d3
83
tb/axis_gmii_rx/Makefile
Normal file
83
tb/axis_gmii_rx/Makefile
Normal file
@ -0,0 +1,83 @@
|
||||
# Copyright (c) 2020 Alex Forencich
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
|
||||
TOPLEVEL_LANG = verilog
|
||||
|
||||
SIM ?= icarus
|
||||
WAVES ?= 0
|
||||
|
||||
COCOTB_HDL_TIMEUNIT = 1ns
|
||||
COCOTB_HDL_TIMEPRECISION = 1ps
|
||||
|
||||
DUT = axis_gmii_rx
|
||||
TOPLEVEL = $(DUT)
|
||||
MODULE = test_$(DUT)
|
||||
VERILOG_SOURCES += ../../rtl/$(DUT).v
|
||||
VERILOG_SOURCES += ../../rtl/lfsr.v
|
||||
|
||||
# module parameters
|
||||
export PARAM_DATA_WIDTH ?= 8
|
||||
export PARAM_PTP_TS_ENABLE ?= 0
|
||||
export PARAM_PTP_TS_WIDTH ?= 96
|
||||
#export PARAM_USER_WIDTH ?= (parameters['PTP_TS_WIDTH'] if parameters['PTP_TS_ENABLE'] else 0) + 1
|
||||
export PARAM_USER_WIDTH ?= 1
|
||||
|
||||
SIM_BUILD ?= sim_build_$(MODULE)
|
||||
|
||||
ifeq ($(SIM), icarus)
|
||||
PLUSARGS += -fst
|
||||
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).DATA_WIDTH=$(PARAM_DATA_WIDTH)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).PTP_TS_ENABLE=$(PARAM_PTP_TS_ENABLE)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).PTP_TS_WIDTH=$(PARAM_PTP_TS_WIDTH)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).USER_WIDTH=$(PARAM_USER_WIDTH)
|
||||
|
||||
ifeq ($(WAVES), 1)
|
||||
VERILOG_SOURCES += iverilog_dump.v
|
||||
COMPILE_ARGS += -s iverilog_dump
|
||||
endif
|
||||
else ifeq ($(SIM), verilator)
|
||||
COMPILE_ARGS += -Wno-SELRANGE -Wno-WIDTH
|
||||
|
||||
COMPILE_ARGS += -GDATA_WIDTH=$(PARAM_DATA_WIDTH)
|
||||
COMPILE_ARGS += -GPTP_TS_ENABLE=$(PARAM_PTP_TS_ENABLE)
|
||||
COMPILE_ARGS += -GPTP_TS_WIDTH=$(PARAM_PTP_TS_WIDTH)
|
||||
COMPILE_ARGS += -GUSER_WIDTH=$(PARAM_USER_WIDTH)
|
||||
|
||||
ifeq ($(WAVES), 1)
|
||||
COMPILE_ARGS += --trace-fst
|
||||
endif
|
||||
endif
|
||||
|
||||
iverilog_dump.v:
|
||||
echo 'module iverilog_dump();' > $@
|
||||
echo 'initial begin' >> $@
|
||||
echo ' $$dumpfile("$(TOPLEVEL).fst");' >> $@
|
||||
echo ' $$dumpvars(0, $(TOPLEVEL));' >> $@
|
||||
echo 'end' >> $@
|
||||
echo 'endmodule' >> $@
|
||||
|
||||
clean::
|
||||
@rm -rf sim_build_*
|
||||
@rm -rf iverilog_dump.v
|
||||
@rm -rf dump.fst $(TOPLEVEL).fst
|
||||
|
||||
include $(shell cocotb-config --makefiles)/Makefile.sim
|
||||
|
182
tb/axis_gmii_rx/test_axis_gmii_rx.py
Normal file
182
tb/axis_gmii_rx/test_axis_gmii_rx.py
Normal file
@ -0,0 +1,182 @@
|
||||
#!/usr/bin/env python
|
||||
"""
|
||||
|
||||
Copyright (c) 2020 Alex Forencich
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
"""
|
||||
|
||||
import itertools
|
||||
import logging
|
||||
import os
|
||||
|
||||
import cocotb_test.simulator
|
||||
|
||||
import cocotb
|
||||
from cocotb.clock import Clock
|
||||
from cocotb.triggers import RisingEdge
|
||||
from cocotb.regression import TestFactory
|
||||
|
||||
from cocotbext.eth import GmiiFrame, GmiiSource
|
||||
from cocotbext.axi import AxiStreamSink
|
||||
|
||||
|
||||
class TB:
|
||||
def __init__(self, dut):
|
||||
self.dut = dut
|
||||
|
||||
self.log = logging.getLogger("cocotb.tb")
|
||||
self.log.setLevel(logging.DEBUG)
|
||||
|
||||
self._enable_generator = None
|
||||
self._enable_cr = None
|
||||
|
||||
cocotb.fork(Clock(dut.clk, 8, units="ns").start())
|
||||
|
||||
self.source = GmiiSource(dut.gmii_rxd, dut.gmii_rx_er, dut.gmii_rx_dv,
|
||||
dut.clk, dut.rst, dut.clk_enable, dut.mii_select)
|
||||
self.sink = AxiStreamSink(dut, "m_axis", dut.clk, dut.rst)
|
||||
|
||||
dut.clk_enable.setimmediatevalue(1)
|
||||
dut.mii_select.setimmediatevalue(0)
|
||||
dut.ptp_ts.setimmediatevalue(0)
|
||||
|
||||
async def reset(self):
|
||||
self.dut.rst.setimmediatevalue(0)
|
||||
await RisingEdge(self.dut.clk)
|
||||
await RisingEdge(self.dut.clk)
|
||||
self.dut.rst <= 1
|
||||
await RisingEdge(self.dut.clk)
|
||||
await RisingEdge(self.dut.clk)
|
||||
self.dut.rst <= 0
|
||||
await RisingEdge(self.dut.clk)
|
||||
await RisingEdge(self.dut.clk)
|
||||
|
||||
def set_enable_generator(self, generator=None):
|
||||
if self._enable_cr is not None:
|
||||
self._enable_cr.kill()
|
||||
self._enable_cr = None
|
||||
|
||||
self._enable_generator = generator
|
||||
|
||||
if self._enable_generator is not None:
|
||||
self._enable_cr = cocotb.fork(self._run_enable())
|
||||
|
||||
def clear_enable_generator(self):
|
||||
self.set_enable_generator(None)
|
||||
|
||||
async def _run_enable(self):
|
||||
for val in self._enable_generator:
|
||||
self.dut.clk_enable <= val
|
||||
await RisingEdge(self.dut.clk)
|
||||
|
||||
|
||||
async def run_test(dut, payload_lengths=None, payload_data=None, ifg=12, enable_gen=None, mii_sel=False):
|
||||
|
||||
tb = TB(dut)
|
||||
|
||||
tb.source.ifg = ifg
|
||||
tb.dut.mii_select <= mii_sel
|
||||
|
||||
if enable_gen is not None:
|
||||
tb.set_enable_generator(enable_gen())
|
||||
|
||||
await tb.reset()
|
||||
|
||||
test_frames = [payload_data(x) for x in payload_lengths()]
|
||||
|
||||
for test_data in test_frames:
|
||||
test_frame = GmiiFrame.from_payload(test_data)
|
||||
await tb.source.send(test_frame)
|
||||
|
||||
for test_data in test_frames:
|
||||
rx_frame = await tb.sink.recv()
|
||||
|
||||
assert rx_frame.tdata == test_data
|
||||
assert rx_frame.tuser == 0
|
||||
|
||||
assert tb.sink.empty()
|
||||
|
||||
await RisingEdge(dut.clk)
|
||||
await RisingEdge(dut.clk)
|
||||
|
||||
|
||||
def size_list():
|
||||
return list(range(60, 128)) + [512, 1514] + [60]*10
|
||||
|
||||
|
||||
def incrementing_payload(length):
|
||||
return bytearray(itertools.islice(itertools.cycle(range(256)), length))
|
||||
|
||||
|
||||
def cycle_en():
|
||||
return itertools.cycle([0, 0, 0, 1])
|
||||
|
||||
|
||||
if cocotb.SIM_NAME:
|
||||
|
||||
factory = TestFactory(run_test)
|
||||
factory.add_option("payload_lengths", [size_list])
|
||||
factory.add_option("payload_data", [incrementing_payload])
|
||||
factory.add_option("ifg", [12, 0])
|
||||
factory.add_option("enable_gen", [None, cycle_en])
|
||||
factory.add_option("mii_sel", [False, True])
|
||||
factory.generate_tests()
|
||||
|
||||
|
||||
# cocotb-test
|
||||
|
||||
tests_dir = os.path.abspath(os.path.dirname(__file__))
|
||||
rtl_dir = os.path.abspath(os.path.join(tests_dir, '..', '..', 'rtl'))
|
||||
lib_dir = os.path.abspath(os.path.join(rtl_dir, '..', 'lib'))
|
||||
axis_rtl_dir = os.path.abspath(os.path.join(lib_dir, 'axis', 'rtl'))
|
||||
|
||||
|
||||
def test_axis_gmii_rx(request):
|
||||
dut = "axis_gmii_rx"
|
||||
module = os.path.splitext(os.path.basename(__file__))[0]
|
||||
toplevel = dut
|
||||
|
||||
verilog_sources = [
|
||||
os.path.join(rtl_dir, f"{dut}.v"),
|
||||
os.path.join(rtl_dir, "lfsr.v"),
|
||||
]
|
||||
|
||||
parameters = {}
|
||||
|
||||
parameters['DATA_WIDTH'] = 8
|
||||
parameters['PTP_TS_ENABLE'] = 0
|
||||
parameters['PTP_TS_WIDTH'] = 96
|
||||
parameters['USER_WIDTH'] = (parameters['PTP_TS_WIDTH'] if parameters['PTP_TS_ENABLE'] else 0) + 1
|
||||
|
||||
extra_env = {f'PARAM_{k}': str(v) for k, v in parameters.items()}
|
||||
|
||||
sim_build = os.path.join(tests_dir,
|
||||
"sim_build_"+request.node.name.replace('[', '-').replace(']', ''))
|
||||
|
||||
cocotb_test.simulator.run(
|
||||
python_search=[tests_dir],
|
||||
verilog_sources=verilog_sources,
|
||||
toplevel=toplevel,
|
||||
module=module,
|
||||
parameters=parameters,
|
||||
sim_build=sim_build,
|
||||
extra_env=extra_env,
|
||||
)
|
95
tb/axis_gmii_tx/Makefile
Normal file
95
tb/axis_gmii_tx/Makefile
Normal file
@ -0,0 +1,95 @@
|
||||
# Copyright (c) 2020 Alex Forencich
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
|
||||
TOPLEVEL_LANG = verilog
|
||||
|
||||
SIM ?= icarus
|
||||
WAVES ?= 0
|
||||
|
||||
COCOTB_HDL_TIMEUNIT = 1ns
|
||||
COCOTB_HDL_TIMEPRECISION = 1ps
|
||||
|
||||
DUT = axis_gmii_tx
|
||||
TOPLEVEL = $(DUT)
|
||||
MODULE = test_$(DUT)
|
||||
VERILOG_SOURCES += ../../rtl/$(DUT).v
|
||||
VERILOG_SOURCES += ../../rtl/lfsr.v
|
||||
|
||||
# module parameters
|
||||
export PARAM_DATA_WIDTH ?= 8
|
||||
export PARAM_ENABLE_PADDING ?= 1
|
||||
export PARAM_MIN_FRAME_LENGTH ?= 64
|
||||
export PARAM_PTP_TS_ENABLE ?= 0
|
||||
export PARAM_PTP_TS_WIDTH ?= 96
|
||||
export PARAM_PTP_TAG_ENABLE ?= PTP_TS_ENABLE
|
||||
export PARAM_PTP_TAG_WIDTH ?= 16
|
||||
#export PARAM_USER_WIDTH ?= (parameters['PTP_TS_WIDTH'] if parameters['PTP_TS_ENABLE'] else 0) + 1
|
||||
export PARAM_USER_WIDTH ?= 1
|
||||
|
||||
SIM_BUILD ?= sim_build_$(MODULE)
|
||||
|
||||
ifeq ($(SIM), icarus)
|
||||
PLUSARGS += -fst
|
||||
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).DATA_WIDTH=$(PARAM_DATA_WIDTH)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).ENABLE_PADDING=$(PARAM_ENABLE_PADDING)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).MIN_FRAME_LENGTH=$(PARAM_MIN_FRAME_LENGTH)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).PTP_TS_ENABLE=$(PARAM_PTP_TS_ENABLE)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).PTP_TS_WIDTH=$(PARAM_PTP_TS_WIDTH)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).PTP_TAG_ENABLE=$(PARAM_PTP_TAG_ENABLE)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).PTP_TAG_WIDTH=$(PARAM_PTP_TAG_WIDTH)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).USER_WIDTH=$(PARAM_USER_WIDTH)
|
||||
|
||||
ifeq ($(WAVES), 1)
|
||||
VERILOG_SOURCES += iverilog_dump.v
|
||||
COMPILE_ARGS += -s iverilog_dump
|
||||
endif
|
||||
else ifeq ($(SIM), verilator)
|
||||
COMPILE_ARGS += -Wno-SELRANGE -Wno-WIDTH
|
||||
|
||||
COMPILE_ARGS += -GDATA_WIDTH=$(PARAM_DATA_WIDTH)
|
||||
COMPILE_ARGS += -GENABLE_PADDING=$(PARAM_ENABLE_PADDING)
|
||||
COMPILE_ARGS += -GMIN_FRAME_LENGTH=$(PARAM_MIN_FRAME_LENGTH)
|
||||
COMPILE_ARGS += -GPTP_TS_ENABLE=$(PARAM_PTP_TS_ENABLE)
|
||||
COMPILE_ARGS += -GPTP_TS_WIDTH=$(PARAM_PTP_TS_WIDTH)
|
||||
COMPILE_ARGS += -GPTP_TAG_ENABLE=$(PARAM_PTP_TAG_ENABLE)
|
||||
COMPILE_ARGS += -GPTP_TAG_WIDTH=$(PARAM_PTP_TAG_WIDTH)
|
||||
COMPILE_ARGS += -GUSER_WIDTH=$(PARAM_USER_WIDTH)
|
||||
|
||||
ifeq ($(WAVES), 1)
|
||||
COMPILE_ARGS += --trace-fst
|
||||
endif
|
||||
endif
|
||||
|
||||
iverilog_dump.v:
|
||||
echo 'module iverilog_dump();' > $@
|
||||
echo 'initial begin' >> $@
|
||||
echo ' $$dumpfile("$(TOPLEVEL).fst");' >> $@
|
||||
echo ' $$dumpvars(0, $(TOPLEVEL));' >> $@
|
||||
echo 'end' >> $@
|
||||
echo 'endmodule' >> $@
|
||||
|
||||
clean::
|
||||
@rm -rf sim_build_*
|
||||
@rm -rf iverilog_dump.v
|
||||
@rm -rf dump.fst $(TOPLEVEL).fst
|
||||
|
||||
include $(shell cocotb-config --makefiles)/Makefile.sim
|
||||
|
187
tb/axis_gmii_tx/test_axis_gmii_tx.py
Normal file
187
tb/axis_gmii_tx/test_axis_gmii_tx.py
Normal file
@ -0,0 +1,187 @@
|
||||
#!/usr/bin/env python
|
||||
"""
|
||||
|
||||
Copyright (c) 2020 Alex Forencich
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
"""
|
||||
|
||||
import itertools
|
||||
import logging
|
||||
import os
|
||||
|
||||
import cocotb_test.simulator
|
||||
|
||||
import cocotb
|
||||
from cocotb.clock import Clock
|
||||
from cocotb.triggers import RisingEdge
|
||||
from cocotb.regression import TestFactory
|
||||
|
||||
from cocotbext.eth import GmiiSink
|
||||
from cocotbext.axi import AxiStreamSource
|
||||
|
||||
|
||||
class TB:
|
||||
def __init__(self, dut):
|
||||
self.dut = dut
|
||||
|
||||
self.log = logging.getLogger("cocotb.tb")
|
||||
self.log.setLevel(logging.DEBUG)
|
||||
|
||||
self._enable_generator = None
|
||||
self._enable_cr = None
|
||||
|
||||
cocotb.fork(Clock(dut.clk, 8, units="ns").start())
|
||||
|
||||
self.source = AxiStreamSource(dut, "s_axis", dut.clk, dut.rst)
|
||||
self.sink = GmiiSink(dut.gmii_txd, dut.gmii_tx_er, dut.gmii_tx_en,
|
||||
dut.clk, dut.rst, dut.clk_enable, dut.mii_select)
|
||||
|
||||
dut.clk_enable.setimmediatevalue(1)
|
||||
dut.mii_select.setimmediatevalue(0)
|
||||
dut.ptp_ts.setimmediatevalue(0)
|
||||
dut.ifg_delay.setimmediatevalue(0)
|
||||
|
||||
async def reset(self):
|
||||
self.dut.rst.setimmediatevalue(0)
|
||||
await RisingEdge(self.dut.clk)
|
||||
await RisingEdge(self.dut.clk)
|
||||
self.dut.rst <= 1
|
||||
await RisingEdge(self.dut.clk)
|
||||
await RisingEdge(self.dut.clk)
|
||||
self.dut.rst <= 0
|
||||
await RisingEdge(self.dut.clk)
|
||||
await RisingEdge(self.dut.clk)
|
||||
|
||||
def set_enable_generator(self, generator=None):
|
||||
if self._enable_cr is not None:
|
||||
self._enable_cr.kill()
|
||||
self._enable_cr = None
|
||||
|
||||
self._enable_generator = generator
|
||||
|
||||
if self._enable_generator is not None:
|
||||
self._enable_cr = cocotb.fork(self._run_enable())
|
||||
|
||||
def clear_enable_generator(self):
|
||||
self.set_enable_generator(None)
|
||||
|
||||
async def _run_enable(self):
|
||||
for val in self._enable_generator:
|
||||
self.dut.clk_enable <= val
|
||||
await RisingEdge(self.dut.clk)
|
||||
|
||||
|
||||
async def run_test(dut, payload_lengths=None, payload_data=None, ifg=12, enable_gen=None, mii_sel=False):
|
||||
|
||||
tb = TB(dut)
|
||||
|
||||
tb.dut.ifg_delay <= ifg
|
||||
tb.dut.mii_select <= mii_sel
|
||||
|
||||
if enable_gen is not None:
|
||||
tb.set_enable_generator(enable_gen())
|
||||
|
||||
await tb.reset()
|
||||
|
||||
test_frames = [payload_data(x) for x in payload_lengths()]
|
||||
|
||||
for test_data in test_frames:
|
||||
await tb.source.send(test_data)
|
||||
|
||||
for test_data in test_frames:
|
||||
rx_frame = await tb.sink.recv()
|
||||
|
||||
assert rx_frame.get_payload() == test_data
|
||||
assert rx_frame.check_fcs()
|
||||
assert rx_frame.error is None
|
||||
|
||||
assert tb.sink.empty()
|
||||
|
||||
await RisingEdge(dut.clk)
|
||||
await RisingEdge(dut.clk)
|
||||
|
||||
|
||||
def size_list():
|
||||
return list(range(60, 128)) + [512, 1514] + [60]*10
|
||||
|
||||
|
||||
def incrementing_payload(length):
|
||||
return bytearray(itertools.islice(itertools.cycle(range(256)), length))
|
||||
|
||||
|
||||
def cycle_en():
|
||||
return itertools.cycle([0, 0, 0, 1])
|
||||
|
||||
|
||||
if cocotb.SIM_NAME:
|
||||
|
||||
factory = TestFactory(run_test)
|
||||
factory.add_option("payload_lengths", [size_list])
|
||||
factory.add_option("payload_data", [incrementing_payload])
|
||||
factory.add_option("ifg", [12])
|
||||
factory.add_option("enable_gen", [None, cycle_en])
|
||||
factory.add_option("mii_sel", [False, True])
|
||||
factory.generate_tests()
|
||||
|
||||
|
||||
# cocotb-test
|
||||
|
||||
tests_dir = os.path.abspath(os.path.dirname(__file__))
|
||||
rtl_dir = os.path.abspath(os.path.join(tests_dir, '..', '..', 'rtl'))
|
||||
lib_dir = os.path.abspath(os.path.join(rtl_dir, '..', 'lib'))
|
||||
axis_rtl_dir = os.path.abspath(os.path.join(lib_dir, 'axis', 'rtl'))
|
||||
|
||||
|
||||
def test_axis_gmii_tx(request):
|
||||
dut = "axis_gmii_tx"
|
||||
module = os.path.splitext(os.path.basename(__file__))[0]
|
||||
toplevel = dut
|
||||
|
||||
verilog_sources = [
|
||||
os.path.join(rtl_dir, f"{dut}.v"),
|
||||
os.path.join(rtl_dir, "lfsr.v"),
|
||||
]
|
||||
|
||||
parameters = {}
|
||||
|
||||
parameters['DATA_WIDTH'] = 8
|
||||
parameters['ENABLE_PADDING'] = 1
|
||||
parameters['MIN_FRAME_LENGTH'] = 64
|
||||
parameters['PTP_TS_ENABLE'] = 0
|
||||
parameters['PTP_TS_WIDTH'] = 96
|
||||
parameters['PTP_TAG_ENABLE'] = parameters['PTP_TS_ENABLE']
|
||||
parameters['PTP_TAG_WIDTH'] = 16
|
||||
parameters['USER_WIDTH'] = (parameters['PTP_TS_WIDTH'] if parameters['PTP_TS_ENABLE'] else 0) + 1
|
||||
|
||||
extra_env = {f'PARAM_{k}': str(v) for k, v in parameters.items()}
|
||||
|
||||
sim_build = os.path.join(tests_dir,
|
||||
"sim_build_"+request.node.name.replace('[', '-').replace(']', ''))
|
||||
|
||||
cocotb_test.simulator.run(
|
||||
python_search=[tests_dir],
|
||||
verilog_sources=verilog_sources,
|
||||
toplevel=toplevel,
|
||||
module=module,
|
||||
parameters=parameters,
|
||||
sim_build=sim_build,
|
||||
extra_env=extra_env,
|
||||
)
|
89
tb/axis_xgmii_rx_32/Makefile
Normal file
89
tb/axis_xgmii_rx_32/Makefile
Normal file
@ -0,0 +1,89 @@
|
||||
# Copyright (c) 2020 Alex Forencich
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
|
||||
TOPLEVEL_LANG = verilog
|
||||
|
||||
SIM ?= icarus
|
||||
WAVES ?= 0
|
||||
|
||||
COCOTB_HDL_TIMEUNIT = 1ns
|
||||
COCOTB_HDL_TIMEPRECISION = 1ps
|
||||
|
||||
DUT = axis_xgmii_rx_32
|
||||
TOPLEVEL = $(DUT)
|
||||
MODULE = test_$(DUT)
|
||||
VERILOG_SOURCES += ../../rtl/$(DUT).v
|
||||
VERILOG_SOURCES += ../../rtl/lfsr.v
|
||||
|
||||
# module parameters
|
||||
export PARAM_DATA_WIDTH ?= 32
|
||||
export PARAM_KEEP_WIDTH ?= $(shell expr $(PARAM_DATA_WIDTH) / 8 )
|
||||
export PARAM_CTRL_WIDTH ?= $(shell expr $(PARAM_DATA_WIDTH) / 8 )
|
||||
export PARAM_PTP_TS_ENABLE ?= 0
|
||||
export PARAM_PTP_TS_WIDTH ?= 96
|
||||
#export PARAM_USER_WIDTH ?= (parameters['PTP_TS_WIDTH'] if parameters['PTP_TS_ENABLE'] else 0) + 1
|
||||
export PARAM_USER_WIDTH ?= 1
|
||||
|
||||
SIM_BUILD ?= sim_build_$(MODULE)
|
||||
|
||||
ifeq ($(SIM), icarus)
|
||||
PLUSARGS += -fst
|
||||
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).DATA_WIDTH=$(PARAM_DATA_WIDTH)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).KEEP_WIDTH=$(PARAM_KEEP_WIDTH)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).CTRL_WIDTH=$(PARAM_CTRL_WIDTH)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).PTP_TS_ENABLE=$(PARAM_PTP_TS_ENABLE)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).PTP_TS_WIDTH=$(PARAM_PTP_TS_WIDTH)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).USER_WIDTH=$(PARAM_USER_WIDTH)
|
||||
|
||||
ifeq ($(WAVES), 1)
|
||||
VERILOG_SOURCES += iverilog_dump.v
|
||||
COMPILE_ARGS += -s iverilog_dump
|
||||
endif
|
||||
else ifeq ($(SIM), verilator)
|
||||
COMPILE_ARGS += -Wno-SELRANGE -Wno-WIDTH
|
||||
|
||||
COMPILE_ARGS += -GDATA_WIDTH=$(PARAM_DATA_WIDTH)
|
||||
COMPILE_ARGS += -GKEEP_WIDTH=$(PARAM_KEEP_WIDTH)
|
||||
COMPILE_ARGS += -GCTRL_WIDTH=$(PARAM_CTRL_WIDTH)
|
||||
COMPILE_ARGS += -GPTP_TS_ENABLE=$(PARAM_PTP_TS_ENABLE)
|
||||
COMPILE_ARGS += -GPTP_TS_WIDTH=$(PARAM_PTP_TS_WIDTH)
|
||||
COMPILE_ARGS += -GUSER_WIDTH=$(PARAM_USER_WIDTH)
|
||||
|
||||
ifeq ($(WAVES), 1)
|
||||
COMPILE_ARGS += --trace-fst
|
||||
endif
|
||||
endif
|
||||
|
||||
iverilog_dump.v:
|
||||
echo 'module iverilog_dump();' > $@
|
||||
echo 'initial begin' >> $@
|
||||
echo ' $$dumpfile("$(TOPLEVEL).fst");' >> $@
|
||||
echo ' $$dumpvars(0, $(TOPLEVEL));' >> $@
|
||||
echo 'end' >> $@
|
||||
echo 'endmodule' >> $@
|
||||
|
||||
clean::
|
||||
@rm -rf sim_build_*
|
||||
@rm -rf iverilog_dump.v
|
||||
@rm -rf dump.fst $(TOPLEVEL).fst
|
||||
|
||||
include $(shell cocotb-config --makefiles)/Makefile.sim
|
||||
|
154
tb/axis_xgmii_rx_32/test_axis_xgmii_rx_32.py
Normal file
154
tb/axis_xgmii_rx_32/test_axis_xgmii_rx_32.py
Normal file
@ -0,0 +1,154 @@
|
||||
#!/usr/bin/env python
|
||||
"""
|
||||
|
||||
Copyright (c) 2020 Alex Forencich
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
"""
|
||||
|
||||
import itertools
|
||||
import logging
|
||||
import os
|
||||
|
||||
import cocotb_test.simulator
|
||||
|
||||
import cocotb
|
||||
from cocotb.clock import Clock
|
||||
from cocotb.triggers import RisingEdge
|
||||
from cocotb.regression import TestFactory
|
||||
|
||||
from cocotbext.eth import XgmiiFrame, XgmiiSource
|
||||
from cocotbext.axi import AxiStreamSink
|
||||
|
||||
|
||||
class TB:
|
||||
def __init__(self, dut):
|
||||
self.dut = dut
|
||||
|
||||
self.log = logging.getLogger("cocotb.tb")
|
||||
self.log.setLevel(logging.DEBUG)
|
||||
|
||||
cocotb.fork(Clock(dut.clk, 3.2, units="ns").start())
|
||||
|
||||
self.source = XgmiiSource(dut.xgmii_rxd, dut.xgmii_rxc, dut.clk, dut.rst)
|
||||
self.sink = AxiStreamSink(dut, "m_axis", dut.clk, dut.rst)
|
||||
|
||||
dut.ptp_ts.setimmediatevalue(0)
|
||||
|
||||
async def reset(self):
|
||||
self.dut.rst.setimmediatevalue(0)
|
||||
await RisingEdge(self.dut.clk)
|
||||
await RisingEdge(self.dut.clk)
|
||||
self.dut.rst <= 1
|
||||
await RisingEdge(self.dut.clk)
|
||||
await RisingEdge(self.dut.clk)
|
||||
self.dut.rst <= 0
|
||||
await RisingEdge(self.dut.clk)
|
||||
await RisingEdge(self.dut.clk)
|
||||
|
||||
|
||||
async def run_test(dut, payload_lengths=None, payload_data=None, ifg=12):
|
||||
|
||||
tb = TB(dut)
|
||||
|
||||
tb.source.ifg = ifg
|
||||
|
||||
await tb.reset()
|
||||
|
||||
test_frames = [payload_data(x) for x in payload_lengths()]
|
||||
|
||||
for test_data in test_frames:
|
||||
test_frame = XgmiiFrame.from_payload(test_data)
|
||||
await tb.source.send(test_frame)
|
||||
|
||||
for test_data in test_frames:
|
||||
rx_frame = await tb.sink.recv()
|
||||
|
||||
assert rx_frame.tdata == test_data
|
||||
assert rx_frame.tuser == 0
|
||||
|
||||
assert tb.sink.empty()
|
||||
|
||||
await RisingEdge(dut.clk)
|
||||
await RisingEdge(dut.clk)
|
||||
|
||||
|
||||
def size_list():
|
||||
return list(range(60, 128)) + [512, 1514, 9214] + [60]*10
|
||||
|
||||
|
||||
def incrementing_payload(length):
|
||||
return bytearray(itertools.islice(itertools.cycle(range(256)), length))
|
||||
|
||||
|
||||
def cycle_en():
|
||||
return itertools.cycle([0, 0, 0, 1])
|
||||
|
||||
|
||||
if cocotb.SIM_NAME:
|
||||
|
||||
factory = TestFactory(run_test)
|
||||
factory.add_option("payload_lengths", [size_list])
|
||||
factory.add_option("payload_data", [incrementing_payload])
|
||||
factory.add_option("ifg", [12, 0])
|
||||
factory.generate_tests()
|
||||
|
||||
|
||||
# cocotb-test
|
||||
|
||||
tests_dir = os.path.abspath(os.path.dirname(__file__))
|
||||
rtl_dir = os.path.abspath(os.path.join(tests_dir, '..', '..', 'rtl'))
|
||||
lib_dir = os.path.abspath(os.path.join(rtl_dir, '..', 'lib'))
|
||||
axis_rtl_dir = os.path.abspath(os.path.join(lib_dir, 'axis', 'rtl'))
|
||||
|
||||
|
||||
def test_axis_xgmii_rx_32(request):
|
||||
dut = "axis_xgmii_rx_32"
|
||||
module = os.path.splitext(os.path.basename(__file__))[0]
|
||||
toplevel = dut
|
||||
|
||||
verilog_sources = [
|
||||
os.path.join(rtl_dir, f"{dut}.v"),
|
||||
os.path.join(rtl_dir, "lfsr.v"),
|
||||
]
|
||||
|
||||
parameters = {}
|
||||
|
||||
parameters['DATA_WIDTH'] = 32
|
||||
parameters['KEEP_WIDTH'] = parameters['DATA_WIDTH'] // 8
|
||||
parameters['CTRL_WIDTH'] = parameters['DATA_WIDTH'] // 8
|
||||
parameters['PTP_TS_ENABLE'] = 0
|
||||
parameters['PTP_TS_WIDTH'] = 96
|
||||
parameters['USER_WIDTH'] = (parameters['PTP_TS_WIDTH'] if parameters['PTP_TS_ENABLE'] else 0) + 1
|
||||
|
||||
extra_env = {f'PARAM_{k}': str(v) for k, v in parameters.items()}
|
||||
|
||||
sim_build = os.path.join(tests_dir,
|
||||
"sim_build_"+request.node.name.replace('[', '-').replace(']', ''))
|
||||
|
||||
cocotb_test.simulator.run(
|
||||
python_search=[tests_dir],
|
||||
verilog_sources=verilog_sources,
|
||||
toplevel=toplevel,
|
||||
module=module,
|
||||
parameters=parameters,
|
||||
sim_build=sim_build,
|
||||
extra_env=extra_env,
|
||||
)
|
89
tb/axis_xgmii_rx_64/Makefile
Normal file
89
tb/axis_xgmii_rx_64/Makefile
Normal file
@ -0,0 +1,89 @@
|
||||
# Copyright (c) 2020 Alex Forencich
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
|
||||
TOPLEVEL_LANG = verilog
|
||||
|
||||
SIM ?= icarus
|
||||
WAVES ?= 0
|
||||
|
||||
COCOTB_HDL_TIMEUNIT = 1ns
|
||||
COCOTB_HDL_TIMEPRECISION = 1ps
|
||||
|
||||
DUT = axis_xgmii_rx_64
|
||||
TOPLEVEL = $(DUT)
|
||||
MODULE = test_$(DUT)
|
||||
VERILOG_SOURCES += ../../rtl/$(DUT).v
|
||||
VERILOG_SOURCES += ../../rtl/lfsr.v
|
||||
|
||||
# module parameters
|
||||
export PARAM_DATA_WIDTH ?= 64
|
||||
export PARAM_KEEP_WIDTH ?= $(shell expr $(PARAM_DATA_WIDTH) / 8 )
|
||||
export PARAM_CTRL_WIDTH ?= $(shell expr $(PARAM_DATA_WIDTH) / 8 )
|
||||
export PARAM_PTP_TS_ENABLE ?= 0
|
||||
export PARAM_PTP_TS_WIDTH ?= 96
|
||||
#export PARAM_USER_WIDTH ?= (parameters['PTP_TS_WIDTH'] if parameters['PTP_TS_ENABLE'] else 0) + 1
|
||||
export PARAM_USER_WIDTH ?= 1
|
||||
|
||||
SIM_BUILD ?= sim_build_$(MODULE)
|
||||
|
||||
ifeq ($(SIM), icarus)
|
||||
PLUSARGS += -fst
|
||||
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).DATA_WIDTH=$(PARAM_DATA_WIDTH)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).KEEP_WIDTH=$(PARAM_KEEP_WIDTH)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).CTRL_WIDTH=$(PARAM_CTRL_WIDTH)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).PTP_TS_ENABLE=$(PARAM_PTP_TS_ENABLE)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).PTP_TS_WIDTH=$(PARAM_PTP_TS_WIDTH)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).USER_WIDTH=$(PARAM_USER_WIDTH)
|
||||
|
||||
ifeq ($(WAVES), 1)
|
||||
VERILOG_SOURCES += iverilog_dump.v
|
||||
COMPILE_ARGS += -s iverilog_dump
|
||||
endif
|
||||
else ifeq ($(SIM), verilator)
|
||||
COMPILE_ARGS += -Wno-SELRANGE -Wno-WIDTH
|
||||
|
||||
COMPILE_ARGS += -GDATA_WIDTH=$(PARAM_DATA_WIDTH)
|
||||
COMPILE_ARGS += -GKEEP_WIDTH=$(PARAM_KEEP_WIDTH)
|
||||
COMPILE_ARGS += -GCTRL_WIDTH=$(PARAM_CTRL_WIDTH)
|
||||
COMPILE_ARGS += -GPTP_TS_ENABLE=$(PARAM_PTP_TS_ENABLE)
|
||||
COMPILE_ARGS += -GPTP_TS_WIDTH=$(PARAM_PTP_TS_WIDTH)
|
||||
COMPILE_ARGS += -GUSER_WIDTH=$(PARAM_USER_WIDTH)
|
||||
|
||||
ifeq ($(WAVES), 1)
|
||||
COMPILE_ARGS += --trace-fst
|
||||
endif
|
||||
endif
|
||||
|
||||
iverilog_dump.v:
|
||||
echo 'module iverilog_dump();' > $@
|
||||
echo 'initial begin' >> $@
|
||||
echo ' $$dumpfile("$(TOPLEVEL).fst");' >> $@
|
||||
echo ' $$dumpvars(0, $(TOPLEVEL));' >> $@
|
||||
echo 'end' >> $@
|
||||
echo 'endmodule' >> $@
|
||||
|
||||
clean::
|
||||
@rm -rf sim_build_*
|
||||
@rm -rf iverilog_dump.v
|
||||
@rm -rf dump.fst $(TOPLEVEL).fst
|
||||
|
||||
include $(shell cocotb-config --makefiles)/Makefile.sim
|
||||
|
154
tb/axis_xgmii_rx_64/test_axis_xgmii_rx_64.py
Normal file
154
tb/axis_xgmii_rx_64/test_axis_xgmii_rx_64.py
Normal file
@ -0,0 +1,154 @@
|
||||
#!/usr/bin/env python
|
||||
"""
|
||||
|
||||
Copyright (c) 2020 Alex Forencich
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
"""
|
||||
|
||||
import itertools
|
||||
import logging
|
||||
import os
|
||||
|
||||
import cocotb_test.simulator
|
||||
|
||||
import cocotb
|
||||
from cocotb.clock import Clock
|
||||
from cocotb.triggers import RisingEdge
|
||||
from cocotb.regression import TestFactory
|
||||
|
||||
from cocotbext.eth import XgmiiFrame, XgmiiSource
|
||||
from cocotbext.axi import AxiStreamSink
|
||||
|
||||
|
||||
class TB:
|
||||
def __init__(self, dut):
|
||||
self.dut = dut
|
||||
|
||||
self.log = logging.getLogger("cocotb.tb")
|
||||
self.log.setLevel(logging.DEBUG)
|
||||
|
||||
cocotb.fork(Clock(dut.clk, 6.4, units="ns").start())
|
||||
|
||||
self.source = XgmiiSource(dut.xgmii_rxd, dut.xgmii_rxc, dut.clk, dut.rst)
|
||||
self.sink = AxiStreamSink(dut, "m_axis", dut.clk, dut.rst)
|
||||
|
||||
dut.ptp_ts.setimmediatevalue(0)
|
||||
|
||||
async def reset(self):
|
||||
self.dut.rst.setimmediatevalue(0)
|
||||
await RisingEdge(self.dut.clk)
|
||||
await RisingEdge(self.dut.clk)
|
||||
self.dut.rst <= 1
|
||||
await RisingEdge(self.dut.clk)
|
||||
await RisingEdge(self.dut.clk)
|
||||
self.dut.rst <= 0
|
||||
await RisingEdge(self.dut.clk)
|
||||
await RisingEdge(self.dut.clk)
|
||||
|
||||
|
||||
async def run_test(dut, payload_lengths=None, payload_data=None, ifg=12):
|
||||
|
||||
tb = TB(dut)
|
||||
|
||||
tb.source.ifg = ifg
|
||||
|
||||
await tb.reset()
|
||||
|
||||
test_frames = [payload_data(x) for x in payload_lengths()]
|
||||
|
||||
for test_data in test_frames:
|
||||
test_frame = XgmiiFrame.from_payload(test_data)
|
||||
await tb.source.send(test_frame)
|
||||
|
||||
for test_data in test_frames:
|
||||
rx_frame = await tb.sink.recv()
|
||||
|
||||
assert rx_frame.tdata == test_data
|
||||
assert rx_frame.tuser == 0
|
||||
|
||||
assert tb.sink.empty()
|
||||
|
||||
await RisingEdge(dut.clk)
|
||||
await RisingEdge(dut.clk)
|
||||
|
||||
|
||||
def size_list():
|
||||
return list(range(60, 128)) + [512, 1514, 9214] + [60]*10
|
||||
|
||||
|
||||
def incrementing_payload(length):
|
||||
return bytearray(itertools.islice(itertools.cycle(range(256)), length))
|
||||
|
||||
|
||||
def cycle_en():
|
||||
return itertools.cycle([0, 0, 0, 1])
|
||||
|
||||
|
||||
if cocotb.SIM_NAME:
|
||||
|
||||
factory = TestFactory(run_test)
|
||||
factory.add_option("payload_lengths", [size_list])
|
||||
factory.add_option("payload_data", [incrementing_payload])
|
||||
factory.add_option("ifg", [12, 0])
|
||||
factory.generate_tests()
|
||||
|
||||
|
||||
# cocotb-test
|
||||
|
||||
tests_dir = os.path.abspath(os.path.dirname(__file__))
|
||||
rtl_dir = os.path.abspath(os.path.join(tests_dir, '..', '..', 'rtl'))
|
||||
lib_dir = os.path.abspath(os.path.join(rtl_dir, '..', 'lib'))
|
||||
axis_rtl_dir = os.path.abspath(os.path.join(lib_dir, 'axis', 'rtl'))
|
||||
|
||||
|
||||
def test_axis_xgmii_rx_64(request):
|
||||
dut = "axis_xgmii_rx_64"
|
||||
module = os.path.splitext(os.path.basename(__file__))[0]
|
||||
toplevel = dut
|
||||
|
||||
verilog_sources = [
|
||||
os.path.join(rtl_dir, f"{dut}.v"),
|
||||
os.path.join(rtl_dir, "lfsr.v"),
|
||||
]
|
||||
|
||||
parameters = {}
|
||||
|
||||
parameters['DATA_WIDTH'] = 64
|
||||
parameters['KEEP_WIDTH'] = parameters['DATA_WIDTH'] // 8
|
||||
parameters['CTRL_WIDTH'] = parameters['DATA_WIDTH'] // 8
|
||||
parameters['PTP_TS_ENABLE'] = 0
|
||||
parameters['PTP_TS_WIDTH'] = 96
|
||||
parameters['USER_WIDTH'] = (parameters['PTP_TS_WIDTH'] if parameters['PTP_TS_ENABLE'] else 0) + 1
|
||||
|
||||
extra_env = {f'PARAM_{k}': str(v) for k, v in parameters.items()}
|
||||
|
||||
sim_build = os.path.join(tests_dir,
|
||||
"sim_build_"+request.node.name.replace('[', '-').replace(']', ''))
|
||||
|
||||
cocotb_test.simulator.run(
|
||||
python_search=[tests_dir],
|
||||
verilog_sources=verilog_sources,
|
||||
toplevel=toplevel,
|
||||
module=module,
|
||||
parameters=parameters,
|
||||
sim_build=sim_build,
|
||||
extra_env=extra_env,
|
||||
)
|
104
tb/axis_xgmii_tx_32/Makefile
Normal file
104
tb/axis_xgmii_tx_32/Makefile
Normal file
@ -0,0 +1,104 @@
|
||||
# Copyright (c) 2020 Alex Forencich
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
|
||||
TOPLEVEL_LANG = verilog
|
||||
|
||||
SIM ?= icarus
|
||||
WAVES ?= 0
|
||||
|
||||
COCOTB_HDL_TIMEUNIT = 1ns
|
||||
COCOTB_HDL_TIMEPRECISION = 1ps
|
||||
|
||||
DUT = axis_xgmii_tx_32
|
||||
TOPLEVEL = $(DUT)
|
||||
MODULE = test_$(DUT)
|
||||
VERILOG_SOURCES += ../../rtl/$(DUT).v
|
||||
VERILOG_SOURCES += ../../rtl/lfsr.v
|
||||
|
||||
# module parameters
|
||||
export PARAM_DATA_WIDTH ?= 32
|
||||
export PARAM_KEEP_WIDTH ?= $(shell expr $(PARAM_DATA_WIDTH) / 8 )
|
||||
export PARAM_CTRL_WIDTH ?= $(shell expr $(PARAM_DATA_WIDTH) / 8 )
|
||||
export PARAM_ENABLE_PADDING ?= 1
|
||||
export PARAM_ENABLE_DIC ?= 1
|
||||
export PARAM_MIN_FRAME_LENGTH ?= 64
|
||||
export PARAM_PTP_TS_ENABLE ?= 0
|
||||
export PARAM_PTP_TS_WIDTH ?= 96
|
||||
export PARAM_PTP_TAG_ENABLE ?= $(PARAM_PTP_TS_ENABLE)
|
||||
export PARAM_PTP_TAG_WIDTH ?= 16
|
||||
#export PARAM_USER_WIDTH ?= (parameters['PTP_TAG_WIDTH'] if parameters['PTP_TAG_ENABLE'] else 0) + 1
|
||||
export PARAM_USER_WIDTH ?= 1
|
||||
|
||||
SIM_BUILD ?= sim_build_$(MODULE)-$(PARAM_ENABLE_DIC)
|
||||
|
||||
ifeq ($(SIM), icarus)
|
||||
PLUSARGS += -fst
|
||||
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).DATA_WIDTH=$(PARAM_DATA_WIDTH)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).KEEP_WIDTH=$(PARAM_KEEP_WIDTH)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).CTRL_WIDTH=$(PARAM_CTRL_WIDTH)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).ENABLE_PADDING=$(PARAM_ENABLE_PADDING)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).ENABLE_DIC=$(PARAM_ENABLE_DIC)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).MIN_FRAME_LENGTH=$(PARAM_MIN_FRAME_LENGTH)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).PTP_TS_ENABLE=$(PARAM_PTP_TS_ENABLE)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).PTP_TS_WIDTH=$(PARAM_PTP_TS_WIDTH)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).PTP_TAG_ENABLE=$(PARAM_PTP_TAG_ENABLE)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).PTP_TAG_WIDTH=$(PARAM_PTP_TAG_WIDTH)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).USER_WIDTH=$(PARAM_USER_WIDTH)
|
||||
|
||||
ifeq ($(WAVES), 1)
|
||||
VERILOG_SOURCES += iverilog_dump.v
|
||||
COMPILE_ARGS += -s iverilog_dump
|
||||
endif
|
||||
else ifeq ($(SIM), verilator)
|
||||
COMPILE_ARGS += -Wno-SELRANGE -Wno-WIDTH
|
||||
|
||||
COMPILE_ARGS += -GDATA_WIDTH=$(PARAM_DATA_WIDTH)
|
||||
COMPILE_ARGS += -GKEEP_WIDTH=$(PARAM_KEEP_WIDTH)
|
||||
COMPILE_ARGS += -GCTRL_WIDTH=$(PARAM_CTRL_WIDTH)
|
||||
COMPILE_ARGS += -GENABLE_PADDING=$(PARAM_ENABLE_PADDING)
|
||||
COMPILE_ARGS += -GENABLE_DIC=$(PARAM_ENABLE_DIC)
|
||||
COMPILE_ARGS += -GMIN_FRAME_LENGTH=$(PARAM_MIN_FRAME_LENGTH)
|
||||
COMPILE_ARGS += -GPTP_TS_ENABLE=$(PARAM_PTP_TS_ENABLE)
|
||||
COMPILE_ARGS += -GPTP_TS_WIDTH=$(PARAM_PTP_TS_WIDTH)
|
||||
COMPILE_ARGS += -GPTP_TAG_ENABLE=$(PARAM_PTP_TAG_ENABLE)
|
||||
COMPILE_ARGS += -GPTP_TAG_WIDTH=$(PARAM_PTP_TAG_WIDTH)
|
||||
COMPILE_ARGS += -GUSER_WIDTH=$(PARAM_USER_WIDTH)
|
||||
|
||||
ifeq ($(WAVES), 1)
|
||||
COMPILE_ARGS += --trace-fst
|
||||
endif
|
||||
endif
|
||||
|
||||
iverilog_dump.v:
|
||||
echo 'module iverilog_dump();' > $@
|
||||
echo 'initial begin' >> $@
|
||||
echo ' $$dumpfile("$(TOPLEVEL).fst");' >> $@
|
||||
echo ' $$dumpvars(0, $(TOPLEVEL));' >> $@
|
||||
echo 'end' >> $@
|
||||
echo 'endmodule' >> $@
|
||||
|
||||
clean::
|
||||
@rm -rf sim_build_*
|
||||
@rm -rf iverilog_dump.v
|
||||
@rm -rf dump.fst $(TOPLEVEL).fst
|
||||
|
||||
include $(shell cocotb-config --makefiles)/Makefile.sim
|
||||
|
235
tb/axis_xgmii_tx_32/test_axis_xgmii_tx_32.py
Normal file
235
tb/axis_xgmii_tx_32/test_axis_xgmii_tx_32.py
Normal file
@ -0,0 +1,235 @@
|
||||
#!/usr/bin/env python
|
||||
"""
|
||||
|
||||
Copyright (c) 2020 Alex Forencich
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
"""
|
||||
|
||||
import itertools
|
||||
import logging
|
||||
import os
|
||||
|
||||
import cocotb_test.simulator
|
||||
import pytest
|
||||
|
||||
import cocotb
|
||||
from cocotb.clock import Clock
|
||||
from cocotb.triggers import RisingEdge
|
||||
from cocotb.regression import TestFactory
|
||||
|
||||
from cocotbext.eth import XgmiiSink
|
||||
from cocotbext.axi import AxiStreamSource
|
||||
|
||||
|
||||
class TB:
|
||||
def __init__(self, dut):
|
||||
self.dut = dut
|
||||
|
||||
self.log = logging.getLogger("cocotb.tb")
|
||||
self.log.setLevel(logging.DEBUG)
|
||||
|
||||
cocotb.fork(Clock(dut.clk, 3.2, units="ns").start())
|
||||
|
||||
self.source = AxiStreamSource(dut, "s_axis", dut.clk, dut.rst)
|
||||
self.sink = XgmiiSink(dut.xgmii_txd, dut.xgmii_txc, dut.clk, dut.rst)
|
||||
|
||||
dut.ifg_delay.setimmediatevalue(0)
|
||||
dut.ptp_ts.setimmediatevalue(0)
|
||||
|
||||
async def reset(self):
|
||||
self.dut.rst.setimmediatevalue(0)
|
||||
await RisingEdge(self.dut.clk)
|
||||
await RisingEdge(self.dut.clk)
|
||||
self.dut.rst <= 1
|
||||
await RisingEdge(self.dut.clk)
|
||||
await RisingEdge(self.dut.clk)
|
||||
self.dut.rst <= 0
|
||||
await RisingEdge(self.dut.clk)
|
||||
await RisingEdge(self.dut.clk)
|
||||
|
||||
|
||||
async def run_test(dut, payload_lengths=None, payload_data=None, ifg=12):
|
||||
|
||||
tb = TB(dut)
|
||||
|
||||
tb.dut.ifg_delay <= ifg
|
||||
|
||||
await tb.reset()
|
||||
|
||||
test_frames = [payload_data(x) for x in payload_lengths()]
|
||||
|
||||
for test_data in test_frames:
|
||||
await tb.source.send(test_data)
|
||||
|
||||
for test_data in test_frames:
|
||||
rx_frame = await tb.sink.recv()
|
||||
|
||||
assert rx_frame.get_payload() == test_data
|
||||
assert rx_frame.check_fcs()
|
||||
|
||||
assert tb.sink.empty()
|
||||
|
||||
await RisingEdge(dut.clk)
|
||||
await RisingEdge(dut.clk)
|
||||
|
||||
|
||||
async def run_test_alignment(dut, payload_data=None, ifg=12):
|
||||
|
||||
enable_dic = int(os.getenv("PARAM_ENABLE_DIC"))
|
||||
|
||||
tb = TB(dut)
|
||||
|
||||
byte_width = tb.source.width // 8
|
||||
|
||||
tb.dut.ifg_delay <= ifg
|
||||
|
||||
for length in range(60, 92):
|
||||
|
||||
await tb.reset()
|
||||
|
||||
test_frames = [payload_data(length) for k in range(10)]
|
||||
start_lane = []
|
||||
|
||||
for test_data in test_frames:
|
||||
await tb.source.send(test_data)
|
||||
|
||||
for test_data in test_frames:
|
||||
rx_frame = await tb.sink.recv()
|
||||
|
||||
assert rx_frame.get_payload() == test_data
|
||||
assert rx_frame.check_fcs()
|
||||
assert rx_frame.ctrl is None
|
||||
|
||||
start_lane.append(rx_frame.rx_start_lane)
|
||||
|
||||
tb.log.info("length: %d", length)
|
||||
tb.log.info("start_lane: %s", start_lane)
|
||||
|
||||
start_lane_ref = []
|
||||
|
||||
# compute expected starting lanes
|
||||
lane = 0
|
||||
deficit_idle_count = 0
|
||||
|
||||
for test_data in test_frames:
|
||||
if ifg == 0:
|
||||
lane = 0
|
||||
|
||||
start_lane_ref.append(lane)
|
||||
lane = (lane + len(test_data)+4+ifg) % byte_width
|
||||
|
||||
if enable_dic:
|
||||
offset = lane % 4
|
||||
if deficit_idle_count+offset >= 4:
|
||||
offset += 4
|
||||
lane = (lane - offset) % byte_width
|
||||
deficit_idle_count = (deficit_idle_count + offset) % 4
|
||||
else:
|
||||
offset = lane % 4
|
||||
if offset > 0:
|
||||
offset += 4
|
||||
lane = (lane - offset) % byte_width
|
||||
|
||||
tb.log.info("start_lane_ref: %s", start_lane_ref)
|
||||
|
||||
assert start_lane_ref == start_lane
|
||||
|
||||
await RisingEdge(dut.clk)
|
||||
|
||||
assert tb.sink.empty()
|
||||
|
||||
await RisingEdge(dut.clk)
|
||||
await RisingEdge(dut.clk)
|
||||
|
||||
|
||||
def size_list():
|
||||
return list(range(60, 128)) + [512, 1514, 9214] + [60]*10
|
||||
|
||||
|
||||
def incrementing_payload(length):
|
||||
return bytearray(itertools.islice(itertools.cycle(range(256)), length))
|
||||
|
||||
|
||||
def cycle_en():
|
||||
return itertools.cycle([0, 0, 0, 1])
|
||||
|
||||
|
||||
if cocotb.SIM_NAME:
|
||||
|
||||
factory = TestFactory(run_test)
|
||||
factory.add_option("payload_lengths", [size_list])
|
||||
factory.add_option("payload_data", [incrementing_payload])
|
||||
factory.add_option("ifg", [12])
|
||||
factory.generate_tests()
|
||||
|
||||
factory = TestFactory(run_test_alignment)
|
||||
factory.add_option("payload_data", [incrementing_payload])
|
||||
factory.add_option("ifg", [12])
|
||||
factory.generate_tests()
|
||||
|
||||
|
||||
# cocotb-test
|
||||
|
||||
tests_dir = os.path.abspath(os.path.dirname(__file__))
|
||||
rtl_dir = os.path.abspath(os.path.join(tests_dir, '..', '..', 'rtl'))
|
||||
lib_dir = os.path.abspath(os.path.join(rtl_dir, '..', 'lib'))
|
||||
axis_rtl_dir = os.path.abspath(os.path.join(lib_dir, 'axis', 'rtl'))
|
||||
|
||||
|
||||
@pytest.mark.parametrize("enable_dic", [1, 0])
|
||||
def test_axis_xgmii_tx_32(request, enable_dic):
|
||||
dut = "axis_xgmii_tx_32"
|
||||
module = os.path.splitext(os.path.basename(__file__))[0]
|
||||
toplevel = dut
|
||||
|
||||
verilog_sources = [
|
||||
os.path.join(rtl_dir, f"{dut}.v"),
|
||||
os.path.join(rtl_dir, "lfsr.v"),
|
||||
]
|
||||
|
||||
parameters = {}
|
||||
|
||||
parameters['DATA_WIDTH'] = 32
|
||||
parameters['KEEP_WIDTH'] = parameters['DATA_WIDTH'] // 8
|
||||
parameters['CTRL_WIDTH'] = parameters['DATA_WIDTH'] // 8
|
||||
parameters['ENABLE_PADDING'] = 1
|
||||
parameters['ENABLE_DIC'] = enable_dic
|
||||
parameters['MIN_FRAME_LENGTH'] = 64
|
||||
parameters['PTP_TS_ENABLE'] = 0
|
||||
parameters['PTP_TS_WIDTH'] = 96
|
||||
parameters['PTP_TAG_ENABLE'] = parameters['PTP_TS_ENABLE']
|
||||
parameters['PTP_TAG_WIDTH'] = 16
|
||||
parameters['USER_WIDTH'] = (parameters['PTP_TAG_WIDTH'] if parameters['PTP_TAG_ENABLE'] else 0) + 1
|
||||
|
||||
extra_env = {f'PARAM_{k}': str(v) for k, v in parameters.items()}
|
||||
|
||||
sim_build = os.path.join(tests_dir,
|
||||
"sim_build_"+request.node.name.replace('[', '-').replace(']', ''))
|
||||
|
||||
cocotb_test.simulator.run(
|
||||
python_search=[tests_dir],
|
||||
verilog_sources=verilog_sources,
|
||||
toplevel=toplevel,
|
||||
module=module,
|
||||
parameters=parameters,
|
||||
sim_build=sim_build,
|
||||
extra_env=extra_env,
|
||||
)
|
104
tb/axis_xgmii_tx_64/Makefile
Normal file
104
tb/axis_xgmii_tx_64/Makefile
Normal file
@ -0,0 +1,104 @@
|
||||
# Copyright (c) 2020 Alex Forencich
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
|
||||
TOPLEVEL_LANG = verilog
|
||||
|
||||
SIM ?= icarus
|
||||
WAVES ?= 0
|
||||
|
||||
COCOTB_HDL_TIMEUNIT = 1ns
|
||||
COCOTB_HDL_TIMEPRECISION = 1ps
|
||||
|
||||
DUT = axis_xgmii_tx_64
|
||||
TOPLEVEL = $(DUT)
|
||||
MODULE = test_$(DUT)
|
||||
VERILOG_SOURCES += ../../rtl/$(DUT).v
|
||||
VERILOG_SOURCES += ../../rtl/lfsr.v
|
||||
|
||||
# module parameters
|
||||
export PARAM_DATA_WIDTH ?= 64
|
||||
export PARAM_KEEP_WIDTH ?= $(shell expr $(PARAM_DATA_WIDTH) / 8 )
|
||||
export PARAM_CTRL_WIDTH ?= $(shell expr $(PARAM_DATA_WIDTH) / 8 )
|
||||
export PARAM_ENABLE_PADDING ?= 1
|
||||
export PARAM_ENABLE_DIC ?= 1
|
||||
export PARAM_MIN_FRAME_LENGTH ?= 64
|
||||
export PARAM_PTP_TS_ENABLE ?= 0
|
||||
export PARAM_PTP_TS_WIDTH ?= 96
|
||||
export PARAM_PTP_TAG_ENABLE ?= $(PARAM_PTP_TS_ENABLE)
|
||||
export PARAM_PTP_TAG_WIDTH ?= 16
|
||||
#export PARAM_USER_WIDTH ?= (parameters['PTP_TAG_WIDTH'] if parameters['PTP_TAG_ENABLE'] else 0) + 1
|
||||
export PARAM_USER_WIDTH ?= 1
|
||||
|
||||
SIM_BUILD ?= sim_build_$(MODULE)-$(PARAM_ENABLE_DIC)
|
||||
|
||||
ifeq ($(SIM), icarus)
|
||||
PLUSARGS += -fst
|
||||
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).DATA_WIDTH=$(PARAM_DATA_WIDTH)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).KEEP_WIDTH=$(PARAM_KEEP_WIDTH)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).CTRL_WIDTH=$(PARAM_CTRL_WIDTH)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).ENABLE_PADDING=$(PARAM_ENABLE_PADDING)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).ENABLE_DIC=$(PARAM_ENABLE_DIC)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).MIN_FRAME_LENGTH=$(PARAM_MIN_FRAME_LENGTH)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).PTP_TS_ENABLE=$(PARAM_PTP_TS_ENABLE)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).PTP_TS_WIDTH=$(PARAM_PTP_TS_WIDTH)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).PTP_TAG_ENABLE=$(PARAM_PTP_TAG_ENABLE)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).PTP_TAG_WIDTH=$(PARAM_PTP_TAG_WIDTH)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).USER_WIDTH=$(PARAM_USER_WIDTH)
|
||||
|
||||
ifeq ($(WAVES), 1)
|
||||
VERILOG_SOURCES += iverilog_dump.v
|
||||
COMPILE_ARGS += -s iverilog_dump
|
||||
endif
|
||||
else ifeq ($(SIM), verilator)
|
||||
COMPILE_ARGS += -Wno-SELRANGE -Wno-WIDTH
|
||||
|
||||
COMPILE_ARGS += -GDATA_WIDTH=$(PARAM_DATA_WIDTH)
|
||||
COMPILE_ARGS += -GKEEP_WIDTH=$(PARAM_KEEP_WIDTH)
|
||||
COMPILE_ARGS += -GCTRL_WIDTH=$(PARAM_CTRL_WIDTH)
|
||||
COMPILE_ARGS += -GENABLE_PADDING=$(PARAM_ENABLE_PADDING)
|
||||
COMPILE_ARGS += -GENABLE_DIC=$(PARAM_ENABLE_DIC)
|
||||
COMPILE_ARGS += -GMIN_FRAME_LENGTH=$(PARAM_MIN_FRAME_LENGTH)
|
||||
COMPILE_ARGS += -GPTP_TS_ENABLE=$(PARAM_PTP_TS_ENABLE)
|
||||
COMPILE_ARGS += -GPTP_TS_WIDTH=$(PARAM_PTP_TS_WIDTH)
|
||||
COMPILE_ARGS += -GPTP_TAG_ENABLE=$(PARAM_PTP_TAG_ENABLE)
|
||||
COMPILE_ARGS += -GPTP_TAG_WIDTH=$(PARAM_PTP_TAG_WIDTH)
|
||||
COMPILE_ARGS += -GUSER_WIDTH=$(PARAM_USER_WIDTH)
|
||||
|
||||
ifeq ($(WAVES), 1)
|
||||
COMPILE_ARGS += --trace-fst
|
||||
endif
|
||||
endif
|
||||
|
||||
iverilog_dump.v:
|
||||
echo 'module iverilog_dump();' > $@
|
||||
echo 'initial begin' >> $@
|
||||
echo ' $$dumpfile("$(TOPLEVEL).fst");' >> $@
|
||||
echo ' $$dumpvars(0, $(TOPLEVEL));' >> $@
|
||||
echo 'end' >> $@
|
||||
echo 'endmodule' >> $@
|
||||
|
||||
clean::
|
||||
@rm -rf sim_build_*
|
||||
@rm -rf iverilog_dump.v
|
||||
@rm -rf dump.fst $(TOPLEVEL).fst
|
||||
|
||||
include $(shell cocotb-config --makefiles)/Makefile.sim
|
||||
|
235
tb/axis_xgmii_tx_64/test_axis_xgmii_tx_64.py
Normal file
235
tb/axis_xgmii_tx_64/test_axis_xgmii_tx_64.py
Normal file
@ -0,0 +1,235 @@
|
||||
#!/usr/bin/env python
|
||||
"""
|
||||
|
||||
Copyright (c) 2020 Alex Forencich
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
"""
|
||||
|
||||
import itertools
|
||||
import logging
|
||||
import os
|
||||
|
||||
import cocotb_test.simulator
|
||||
import pytest
|
||||
|
||||
import cocotb
|
||||
from cocotb.clock import Clock
|
||||
from cocotb.triggers import RisingEdge
|
||||
from cocotb.regression import TestFactory
|
||||
|
||||
from cocotbext.eth import XgmiiSink
|
||||
from cocotbext.axi import AxiStreamSource
|
||||
|
||||
|
||||
class TB:
|
||||
def __init__(self, dut):
|
||||
self.dut = dut
|
||||
|
||||
self.log = logging.getLogger("cocotb.tb")
|
||||
self.log.setLevel(logging.DEBUG)
|
||||
|
||||
cocotb.fork(Clock(dut.clk, 6.4, units="ns").start())
|
||||
|
||||
self.source = AxiStreamSource(dut, "s_axis", dut.clk, dut.rst)
|
||||
self.sink = XgmiiSink(dut.xgmii_txd, dut.xgmii_txc, dut.clk, dut.rst)
|
||||
|
||||
dut.ifg_delay.setimmediatevalue(0)
|
||||
dut.ptp_ts.setimmediatevalue(0)
|
||||
|
||||
async def reset(self):
|
||||
self.dut.rst.setimmediatevalue(0)
|
||||
await RisingEdge(self.dut.clk)
|
||||
await RisingEdge(self.dut.clk)
|
||||
self.dut.rst <= 1
|
||||
await RisingEdge(self.dut.clk)
|
||||
await RisingEdge(self.dut.clk)
|
||||
self.dut.rst <= 0
|
||||
await RisingEdge(self.dut.clk)
|
||||
await RisingEdge(self.dut.clk)
|
||||
|
||||
|
||||
async def run_test(dut, payload_lengths=None, payload_data=None, ifg=12):
|
||||
|
||||
tb = TB(dut)
|
||||
|
||||
tb.dut.ifg_delay <= ifg
|
||||
|
||||
await tb.reset()
|
||||
|
||||
test_frames = [payload_data(x) for x in payload_lengths()]
|
||||
|
||||
for test_data in test_frames:
|
||||
await tb.source.send(test_data)
|
||||
|
||||
for test_data in test_frames:
|
||||
rx_frame = await tb.sink.recv()
|
||||
|
||||
assert rx_frame.get_payload() == test_data
|
||||
assert rx_frame.check_fcs()
|
||||
|
||||
assert tb.sink.empty()
|
||||
|
||||
await RisingEdge(dut.clk)
|
||||
await RisingEdge(dut.clk)
|
||||
|
||||
|
||||
async def run_test_alignment(dut, payload_data=None, ifg=12):
|
||||
|
||||
enable_dic = int(os.getenv("PARAM_ENABLE_DIC"))
|
||||
|
||||
tb = TB(dut)
|
||||
|
||||
byte_width = tb.source.width // 8
|
||||
|
||||
tb.dut.ifg_delay <= ifg
|
||||
|
||||
for length in range(60, 92):
|
||||
|
||||
await tb.reset()
|
||||
|
||||
test_frames = [payload_data(length) for k in range(10)]
|
||||
start_lane = []
|
||||
|
||||
for test_data in test_frames:
|
||||
await tb.source.send(test_data)
|
||||
|
||||
for test_data in test_frames:
|
||||
rx_frame = await tb.sink.recv()
|
||||
|
||||
assert rx_frame.get_payload() == test_data
|
||||
assert rx_frame.check_fcs()
|
||||
assert rx_frame.ctrl is None
|
||||
|
||||
start_lane.append(rx_frame.rx_start_lane)
|
||||
|
||||
tb.log.info("length: %d", length)
|
||||
tb.log.info("start_lane: %s", start_lane)
|
||||
|
||||
start_lane_ref = []
|
||||
|
||||
# compute expected starting lanes
|
||||
lane = 0
|
||||
deficit_idle_count = 0
|
||||
|
||||
for test_data in test_frames:
|
||||
if ifg == 0:
|
||||
lane = 0
|
||||
|
||||
start_lane_ref.append(lane)
|
||||
lane = (lane + len(test_data)+4+ifg) % byte_width
|
||||
|
||||
if enable_dic:
|
||||
offset = lane % 4
|
||||
if deficit_idle_count+offset >= 4:
|
||||
offset += 4
|
||||
lane = (lane - offset) % byte_width
|
||||
deficit_idle_count = (deficit_idle_count + offset) % 4
|
||||
else:
|
||||
offset = lane % 4
|
||||
if offset > 0:
|
||||
offset += 4
|
||||
lane = (lane - offset) % byte_width
|
||||
|
||||
tb.log.info("start_lane_ref: %s", start_lane_ref)
|
||||
|
||||
assert start_lane_ref == start_lane
|
||||
|
||||
await RisingEdge(dut.clk)
|
||||
|
||||
assert tb.sink.empty()
|
||||
|
||||
await RisingEdge(dut.clk)
|
||||
await RisingEdge(dut.clk)
|
||||
|
||||
|
||||
def size_list():
|
||||
return list(range(60, 128)) + [512, 1514, 9214] + [60]*10
|
||||
|
||||
|
||||
def incrementing_payload(length):
|
||||
return bytearray(itertools.islice(itertools.cycle(range(256)), length))
|
||||
|
||||
|
||||
def cycle_en():
|
||||
return itertools.cycle([0, 0, 0, 1])
|
||||
|
||||
|
||||
if cocotb.SIM_NAME:
|
||||
|
||||
factory = TestFactory(run_test)
|
||||
factory.add_option("payload_lengths", [size_list])
|
||||
factory.add_option("payload_data", [incrementing_payload])
|
||||
factory.add_option("ifg", [12])
|
||||
factory.generate_tests()
|
||||
|
||||
factory = TestFactory(run_test_alignment)
|
||||
factory.add_option("payload_data", [incrementing_payload])
|
||||
factory.add_option("ifg", [12])
|
||||
factory.generate_tests()
|
||||
|
||||
|
||||
# cocotb-test
|
||||
|
||||
tests_dir = os.path.abspath(os.path.dirname(__file__))
|
||||
rtl_dir = os.path.abspath(os.path.join(tests_dir, '..', '..', 'rtl'))
|
||||
lib_dir = os.path.abspath(os.path.join(rtl_dir, '..', 'lib'))
|
||||
axis_rtl_dir = os.path.abspath(os.path.join(lib_dir, 'axis', 'rtl'))
|
||||
|
||||
|
||||
@pytest.mark.parametrize("enable_dic", [1, 0])
|
||||
def test_axis_xgmii_tx_64(request, enable_dic):
|
||||
dut = "axis_xgmii_tx_64"
|
||||
module = os.path.splitext(os.path.basename(__file__))[0]
|
||||
toplevel = dut
|
||||
|
||||
verilog_sources = [
|
||||
os.path.join(rtl_dir, f"{dut}.v"),
|
||||
os.path.join(rtl_dir, "lfsr.v"),
|
||||
]
|
||||
|
||||
parameters = {}
|
||||
|
||||
parameters['DATA_WIDTH'] = 64
|
||||
parameters['KEEP_WIDTH'] = parameters['DATA_WIDTH'] // 8
|
||||
parameters['CTRL_WIDTH'] = parameters['DATA_WIDTH'] // 8
|
||||
parameters['ENABLE_PADDING'] = 1
|
||||
parameters['ENABLE_DIC'] = enable_dic
|
||||
parameters['MIN_FRAME_LENGTH'] = 64
|
||||
parameters['PTP_TS_ENABLE'] = 0
|
||||
parameters['PTP_TS_WIDTH'] = 96
|
||||
parameters['PTP_TAG_ENABLE'] = parameters['PTP_TS_ENABLE']
|
||||
parameters['PTP_TAG_WIDTH'] = 16
|
||||
parameters['USER_WIDTH'] = (parameters['PTP_TAG_WIDTH'] if parameters['PTP_TAG_ENABLE'] else 0) + 1
|
||||
|
||||
extra_env = {f'PARAM_{k}': str(v) for k, v in parameters.items()}
|
||||
|
||||
sim_build = os.path.join(tests_dir,
|
||||
"sim_build_"+request.node.name.replace('[', '-').replace(']', ''))
|
||||
|
||||
cocotb_test.simulator.run(
|
||||
python_search=[tests_dir],
|
||||
verilog_sources=verilog_sources,
|
||||
toplevel=toplevel,
|
||||
module=module,
|
||||
parameters=parameters,
|
||||
sim_build=sim_build,
|
||||
extra_env=extra_env,
|
||||
)
|
124
tb/eth_mac_10g/Makefile
Normal file
124
tb/eth_mac_10g/Makefile
Normal file
@ -0,0 +1,124 @@
|
||||
# Copyright (c) 2020 Alex Forencich
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
|
||||
TOPLEVEL_LANG = verilog
|
||||
|
||||
SIM ?= icarus
|
||||
WAVES ?= 0
|
||||
|
||||
COCOTB_HDL_TIMEUNIT = 1ns
|
||||
COCOTB_HDL_TIMEPRECISION = 1ps
|
||||
|
||||
DUT = eth_mac_10g
|
||||
TOPLEVEL = $(DUT)
|
||||
MODULE = test_$(DUT)
|
||||
VERILOG_SOURCES += ../../rtl/$(DUT).v
|
||||
VERILOG_SOURCES += ../../rtl/axis_xgmii_rx_32.v
|
||||
VERILOG_SOURCES += ../../rtl/axis_xgmii_tx_32.v
|
||||
VERILOG_SOURCES += ../../rtl/axis_xgmii_rx_64.v
|
||||
VERILOG_SOURCES += ../../rtl/axis_xgmii_tx_64.v
|
||||
VERILOG_SOURCES += ../../rtl/lfsr.v
|
||||
|
||||
# module parameters
|
||||
export PARAM_DATA_WIDTH ?= 64
|
||||
export PARAM_KEEP_WIDTH ?= $(shell expr $(PARAM_DATA_WIDTH) / 8 )
|
||||
export PARAM_CTRL_WIDTH ?= $(shell expr $(PARAM_DATA_WIDTH) / 8 )
|
||||
export PARAM_ENABLE_PADDING ?= 1
|
||||
export PARAM_ENABLE_DIC ?= 1
|
||||
export PARAM_MIN_FRAME_LENGTH ?= 64
|
||||
export PARAM_PTP_PERIOD_NS ?= 6
|
||||
export PARAM_PTP_PERIOD_FNS ?= 26214
|
||||
export PARAM_TX_PTP_TS_ENABLE ?= 0
|
||||
export PARAM_TX_PTP_TS_WIDTH ?= 96
|
||||
export PARAM_TX_PTP_TAG_ENABLE ?= $(PARAM_TX_PTP_TS_ENABLE)
|
||||
export PARAM_TX_PTP_TAG_WIDTH ?= 16
|
||||
export PARAM_RX_PTP_TS_ENABLE ?= 0
|
||||
export PARAM_RX_PTP_TS_WIDTH ?= 96
|
||||
# export PARAM_TX_USER_WIDTH ?= (parameters['TX_PTP_TAG_WIDTH'] if parameters['TX_PTP_TAG_ENABLE'] else 0) + 1
|
||||
export PARAM_TX_USER_WIDTH ?= 1
|
||||
# export PARAM_RX_USER_WIDTH ?= (parameters['RX_PTP_TS_WIDTH'] if parameters['RX_PTP_TS_ENABLE'] else 0) + 1
|
||||
export PARAM_RX_USER_WIDTH ?= 1
|
||||
|
||||
SIM_BUILD ?= sim_build_$(MODULE)-$(PARAM_DATA_WIDTH)-$(PARAM_ENABLE_DIC)
|
||||
|
||||
ifeq ($(SIM), icarus)
|
||||
PLUSARGS += -fst
|
||||
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).DATA_WIDTH=$(PARAM_DATA_WIDTH)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).KEEP_WIDTH=$(PARAM_KEEP_WIDTH)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).CTRL_WIDTH=$(PARAM_CTRL_WIDTH)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).ENABLE_PADDING=$(PARAM_ENABLE_PADDING)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).ENABLE_DIC=$(PARAM_ENABLE_DIC)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).MIN_FRAME_LENGTH=$(PARAM_MIN_FRAME_LENGTH)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).PTP_PERIOD_NS=$(PARAM_PTP_PERIOD_NS)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).PTP_PERIOD_FNS=$(PARAM_PTP_PERIOD_FNS)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).TX_PTP_TS_ENABLE=$(PARAM_TX_PTP_TS_ENABLE)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).TX_PTP_TS_WIDTH=$(PARAM_TX_PTP_TS_WIDTH)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).TX_PTP_TAG_ENABLE=$(PARAM_TX_PTP_TAG_ENABLE)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).TX_PTP_TAG_WIDTH=$(PARAM_TX_PTP_TAG_WIDTH)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).RX_PTP_TS_ENABLE=$(PARAM_RX_PTP_TS_ENABLE)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).RX_PTP_TS_WIDTH=$(PARAM_RX_PTP_TS_WIDTH)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).TX_USER_WIDTH=$(PARAM_TX_USER_WIDTH)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).RX_USER_WIDTH=$(PARAM_RX_USER_WIDTH)
|
||||
|
||||
ifeq ($(WAVES), 1)
|
||||
VERILOG_SOURCES += iverilog_dump.v
|
||||
COMPILE_ARGS += -s iverilog_dump
|
||||
endif
|
||||
else ifeq ($(SIM), verilator)
|
||||
COMPILE_ARGS += -Wno-SELRANGE -Wno-WIDTH
|
||||
|
||||
COMPILE_ARGS += -GDATA_WIDTH=$(PARAM_DATA_WIDTH)
|
||||
COMPILE_ARGS += -GKEEP_WIDTH=$(PARAM_KEEP_WIDTH)
|
||||
COMPILE_ARGS += -GCTRL_WIDTH=$(PARAM_CTRL_WIDTH)
|
||||
COMPILE_ARGS += -GENABLE_PADDING=$(PARAM_ENABLE_PADDING)
|
||||
COMPILE_ARGS += -GENABLE_DIC=$(PARAM_ENABLE_DIC)
|
||||
COMPILE_ARGS += -GMIN_FRAME_LENGTH=$(PARAM_MIN_FRAME_LENGTH)
|
||||
COMPILE_ARGS += -GPTP_PERIOD_NS=$(PARAM_PTP_PERIOD_NS)
|
||||
COMPILE_ARGS += -GPTP_PERIOD_FNS=$(PARAM_PTP_PERIOD_FNS)
|
||||
COMPILE_ARGS += -GTX_PTP_TS_ENABLE=$(PARAM_TX_PTP_TS_ENABLE)
|
||||
COMPILE_ARGS += -GTX_PTP_TS_WIDTH=$(PARAM_TX_PTP_TS_WIDTH)
|
||||
COMPILE_ARGS += -GTX_PTP_TAG_ENABLE=$(PARAM_TX_PTP_TAG_ENABLE)
|
||||
COMPILE_ARGS += -GTX_PTP_TAG_WIDTH=$(PARAM_TX_PTP_TAG_WIDTH)
|
||||
COMPILE_ARGS += -GRX_PTP_TS_ENABLE=$(PARAM_RX_PTP_TS_ENABLE)
|
||||
COMPILE_ARGS += -GRX_PTP_TS_WIDTH=$(PARAM_RX_PTP_TS_WIDTH)
|
||||
COMPILE_ARGS += -GTX_USER_WIDTH=$(PARAM_TX_USER_WIDTH)
|
||||
COMPILE_ARGS += -GRX_USER_WIDTH=$(PARAM_RX_USER_WIDTH)
|
||||
|
||||
ifeq ($(WAVES), 1)
|
||||
COMPILE_ARGS += --trace-fst
|
||||
endif
|
||||
endif
|
||||
|
||||
iverilog_dump.v:
|
||||
echo 'module iverilog_dump();' > $@
|
||||
echo 'initial begin' >> $@
|
||||
echo ' $$dumpfile("$(TOPLEVEL).fst");' >> $@
|
||||
echo ' $$dumpvars(0, $(TOPLEVEL));' >> $@
|
||||
echo 'end' >> $@
|
||||
echo 'endmodule' >> $@
|
||||
|
||||
clean::
|
||||
@rm -rf sim_build_*
|
||||
@rm -rf iverilog_dump.v
|
||||
@rm -rf dump.fst $(TOPLEVEL).fst
|
||||
|
||||
include $(shell cocotb-config --makefiles)/Makefile.sim
|
||||
|
287
tb/eth_mac_10g/test_eth_mac_10g.py
Normal file
287
tb/eth_mac_10g/test_eth_mac_10g.py
Normal file
@ -0,0 +1,287 @@
|
||||
#!/usr/bin/env python
|
||||
"""
|
||||
|
||||
Copyright (c) 2020 Alex Forencich
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
"""
|
||||
|
||||
import itertools
|
||||
import logging
|
||||
import os
|
||||
|
||||
import pytest
|
||||
import cocotb_test.simulator
|
||||
|
||||
import cocotb
|
||||
from cocotb.clock import Clock
|
||||
from cocotb.triggers import RisingEdge
|
||||
from cocotb.regression import TestFactory
|
||||
|
||||
from cocotbext.eth import XgmiiFrame, XgmiiSource, XgmiiSink
|
||||
from cocotbext.axi import AxiStreamSource, AxiStreamSink
|
||||
|
||||
|
||||
class TB:
|
||||
def __init__(self, dut):
|
||||
self.dut = dut
|
||||
|
||||
self.log = logging.getLogger("cocotb.tb")
|
||||
self.log.setLevel(logging.DEBUG)
|
||||
|
||||
if len(dut.xgmii_txd) == 64:
|
||||
cocotb.fork(Clock(dut.rx_clk, 6.4, units="ns").start())
|
||||
cocotb.fork(Clock(dut.tx_clk, 6.4, units="ns").start())
|
||||
else:
|
||||
cocotb.fork(Clock(dut.rx_clk, 3.2, units="ns").start())
|
||||
cocotb.fork(Clock(dut.tx_clk, 3.2, units="ns").start())
|
||||
|
||||
self.xgmii_source = XgmiiSource(dut.xgmii_rxd, dut.xgmii_rxc, dut.rx_clk, dut.rx_rst)
|
||||
self.xgmii_sink = XgmiiSink(dut.xgmii_txd, dut.xgmii_txc, dut.tx_clk, dut.tx_rst)
|
||||
|
||||
self.axis_source = AxiStreamSource(dut, "tx_axis", dut.tx_clk, dut.tx_rst)
|
||||
self.axis_sink = AxiStreamSink(dut, "rx_axis", dut.rx_clk, dut.rx_rst)
|
||||
|
||||
dut.rx_ptp_ts.setimmediatevalue(0)
|
||||
dut.tx_ptp_ts.setimmediatevalue(0)
|
||||
|
||||
async def reset(self):
|
||||
self.dut.rx_rst.setimmediatevalue(0)
|
||||
self.dut.tx_rst.setimmediatevalue(0)
|
||||
await RisingEdge(self.dut.rx_clk)
|
||||
await RisingEdge(self.dut.rx_clk)
|
||||
self.dut.rx_rst <= 1
|
||||
self.dut.tx_rst <= 1
|
||||
await RisingEdge(self.dut.rx_clk)
|
||||
await RisingEdge(self.dut.rx_clk)
|
||||
self.dut.rx_rst <= 0
|
||||
self.dut.tx_rst <= 0
|
||||
await RisingEdge(self.dut.rx_clk)
|
||||
await RisingEdge(self.dut.rx_clk)
|
||||
|
||||
|
||||
async def run_test_rx(dut, payload_lengths=None, payload_data=None, ifg=12):
|
||||
|
||||
tb = TB(dut)
|
||||
|
||||
tb.xgmii_source.ifg = ifg
|
||||
tb.dut.ifg_delay <= ifg
|
||||
|
||||
await tb.reset()
|
||||
|
||||
test_frames = [payload_data(x) for x in payload_lengths()]
|
||||
|
||||
for test_data in test_frames:
|
||||
test_frame = XgmiiFrame.from_payload(test_data)
|
||||
await tb.xgmii_source.send(test_frame)
|
||||
|
||||
for test_data in test_frames:
|
||||
rx_frame = await tb.axis_sink.recv()
|
||||
|
||||
assert rx_frame.tdata == test_data
|
||||
assert rx_frame.tuser == 0
|
||||
|
||||
assert tb.axis_sink.empty()
|
||||
|
||||
await RisingEdge(dut.rx_clk)
|
||||
await RisingEdge(dut.rx_clk)
|
||||
|
||||
|
||||
async def run_test_tx(dut, payload_lengths=None, payload_data=None, ifg=12):
|
||||
|
||||
tb = TB(dut)
|
||||
|
||||
tb.xgmii_source.ifg = ifg
|
||||
tb.dut.ifg_delay <= ifg
|
||||
|
||||
await tb.reset()
|
||||
|
||||
test_frames = [payload_data(x) for x in payload_lengths()]
|
||||
|
||||
for test_data in test_frames:
|
||||
await tb.axis_source.send(test_data)
|
||||
|
||||
for test_data in test_frames:
|
||||
rx_frame = await tb.xgmii_sink.recv()
|
||||
|
||||
assert rx_frame.get_payload() == test_data
|
||||
assert rx_frame.check_fcs()
|
||||
|
||||
assert tb.xgmii_sink.empty()
|
||||
|
||||
await RisingEdge(dut.tx_clk)
|
||||
await RisingEdge(dut.tx_clk)
|
||||
|
||||
|
||||
async def run_test_tx_alignment(dut, payload_data=None, ifg=12):
|
||||
|
||||
enable_dic = int(os.getenv("PARAM_ENABLE_DIC"))
|
||||
|
||||
tb = TB(dut)
|
||||
|
||||
byte_width = tb.axis_source.width // 8
|
||||
|
||||
tb.xgmii_source.ifg = ifg
|
||||
tb.dut.ifg_delay <= ifg
|
||||
|
||||
for length in range(60, 92):
|
||||
|
||||
await tb.reset()
|
||||
|
||||
test_frames = [payload_data(length) for k in range(10)]
|
||||
start_lane = []
|
||||
|
||||
for test_data in test_frames:
|
||||
await tb.axis_source.send(test_data)
|
||||
|
||||
for test_data in test_frames:
|
||||
rx_frame = await tb.xgmii_sink.recv()
|
||||
|
||||
assert rx_frame.get_payload() == test_data
|
||||
assert rx_frame.check_fcs()
|
||||
assert rx_frame.ctrl is None
|
||||
|
||||
start_lane.append(rx_frame.rx_start_lane)
|
||||
|
||||
tb.log.info("length: %d", length)
|
||||
tb.log.info("start_lane: %s", start_lane)
|
||||
|
||||
start_lane_ref = []
|
||||
|
||||
# compute expected starting lanes
|
||||
lane = 0
|
||||
deficit_idle_count = 0
|
||||
|
||||
for test_data in test_frames:
|
||||
if ifg == 0:
|
||||
lane = 0
|
||||
|
||||
start_lane_ref.append(lane)
|
||||
lane = (lane + len(test_data)+4+ifg) % byte_width
|
||||
|
||||
if enable_dic:
|
||||
offset = lane % 4
|
||||
if deficit_idle_count+offset >= 4:
|
||||
offset += 4
|
||||
lane = (lane - offset) % byte_width
|
||||
deficit_idle_count = (deficit_idle_count + offset) % 4
|
||||
else:
|
||||
offset = lane % 4
|
||||
if offset > 0:
|
||||
offset += 4
|
||||
lane = (lane - offset) % byte_width
|
||||
|
||||
tb.log.info("start_lane_ref: %s", start_lane_ref)
|
||||
|
||||
assert start_lane_ref == start_lane
|
||||
|
||||
await RisingEdge(dut.tx_clk)
|
||||
|
||||
assert tb.xgmii_sink.empty()
|
||||
|
||||
await RisingEdge(dut.tx_clk)
|
||||
await RisingEdge(dut.tx_clk)
|
||||
|
||||
|
||||
def size_list():
|
||||
return list(range(60, 128)) + [512, 1514, 9214] + [60]*10
|
||||
|
||||
|
||||
def incrementing_payload(length):
|
||||
return bytearray(itertools.islice(itertools.cycle(range(256)), length))
|
||||
|
||||
|
||||
def cycle_en():
|
||||
return itertools.cycle([0, 0, 0, 1])
|
||||
|
||||
|
||||
if cocotb.SIM_NAME:
|
||||
|
||||
for test in [run_test_rx, run_test_tx]:
|
||||
|
||||
factory = TestFactory(test)
|
||||
factory.add_option("payload_lengths", [size_list])
|
||||
factory.add_option("payload_data", [incrementing_payload])
|
||||
factory.add_option("ifg", [12, 0])
|
||||
factory.generate_tests()
|
||||
|
||||
factory = TestFactory(run_test_tx_alignment)
|
||||
factory.add_option("payload_data", [incrementing_payload])
|
||||
factory.add_option("ifg", [12])
|
||||
factory.generate_tests()
|
||||
|
||||
|
||||
# cocotb-test
|
||||
|
||||
tests_dir = os.path.abspath(os.path.dirname(__file__))
|
||||
rtl_dir = os.path.abspath(os.path.join(tests_dir, '..', '..', 'rtl'))
|
||||
lib_dir = os.path.abspath(os.path.join(rtl_dir, '..', 'lib'))
|
||||
axis_rtl_dir = os.path.abspath(os.path.join(lib_dir, 'axis', 'rtl'))
|
||||
|
||||
|
||||
@pytest.mark.parametrize("enable_dic", [1, 0])
|
||||
@pytest.mark.parametrize("data_width", [32, 64])
|
||||
def test_eth_mac_10g(request, data_width, enable_dic):
|
||||
dut = "eth_mac_10g"
|
||||
module = os.path.splitext(os.path.basename(__file__))[0]
|
||||
toplevel = dut
|
||||
|
||||
verilog_sources = [
|
||||
os.path.join(rtl_dir, f"{dut}.v"),
|
||||
os.path.join(rtl_dir, "axis_xgmii_rx_32.v"),
|
||||
os.path.join(rtl_dir, "axis_xgmii_rx_64.v"),
|
||||
os.path.join(rtl_dir, "axis_xgmii_tx_32.v"),
|
||||
os.path.join(rtl_dir, "axis_xgmii_tx_64.v"),
|
||||
os.path.join(rtl_dir, "lfsr.v"),
|
||||
]
|
||||
|
||||
parameters = {}
|
||||
|
||||
parameters['DATA_WIDTH'] = data_width
|
||||
parameters['KEEP_WIDTH'] = parameters['DATA_WIDTH'] // 8
|
||||
parameters['CTRL_WIDTH'] = parameters['DATA_WIDTH'] // 8
|
||||
parameters['ENABLE_PADDING'] = 1
|
||||
parameters['ENABLE_DIC'] = enable_dic
|
||||
parameters['MIN_FRAME_LENGTH'] = 64
|
||||
parameters['PTP_PERIOD_NS'] = 0x6 if parameters['DATA_WIDTH'] == 64 else 0x3
|
||||
parameters['PTP_PERIOD_FNS'] = 0x6666 if parameters['DATA_WIDTH'] == 64 else 0x3333
|
||||
parameters['TX_PTP_TS_ENABLE'] = 0
|
||||
parameters['TX_PTP_TS_WIDTH'] = 96
|
||||
parameters['TX_PTP_TAG_ENABLE'] = parameters['TX_PTP_TS_ENABLE']
|
||||
parameters['TX_PTP_TAG_WIDTH'] = 16
|
||||
parameters['RX_PTP_TS_ENABLE'] = 0
|
||||
parameters['RX_PTP_TS_WIDTH'] = 96
|
||||
parameters['TX_USER_WIDTH'] = (parameters['TX_PTP_TAG_WIDTH'] if parameters['TX_PTP_TAG_ENABLE'] else 0) + 1
|
||||
parameters['RX_USER_WIDTH'] = (parameters['RX_PTP_TS_WIDTH'] if parameters['RX_PTP_TS_ENABLE'] else 0) + 1
|
||||
|
||||
extra_env = {f'PARAM_{k}': str(v) for k, v in parameters.items()}
|
||||
|
||||
sim_build = os.path.join(tests_dir,
|
||||
"sim_build_"+request.node.name.replace('[', '-').replace(']', ''))
|
||||
|
||||
cocotb_test.simulator.run(
|
||||
python_search=[tests_dir],
|
||||
verilog_sources=verilog_sources,
|
||||
toplevel=toplevel,
|
||||
module=module,
|
||||
parameters=parameters,
|
||||
sim_build=sim_build,
|
||||
extra_env=extra_env,
|
||||
)
|
168
tb/eth_mac_10g_fifo/Makefile
Normal file
168
tb/eth_mac_10g_fifo/Makefile
Normal file
@ -0,0 +1,168 @@
|
||||
# Copyright (c) 2020 Alex Forencich
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
|
||||
TOPLEVEL_LANG = verilog
|
||||
|
||||
SIM ?= icarus
|
||||
WAVES ?= 0
|
||||
|
||||
COCOTB_HDL_TIMEUNIT = 1ns
|
||||
COCOTB_HDL_TIMEPRECISION = 1ps
|
||||
|
||||
DUT = eth_mac_10g_fifo
|
||||
TOPLEVEL = $(DUT)
|
||||
MODULE = test_$(DUT)
|
||||
VERILOG_SOURCES += ../../rtl/$(DUT).v
|
||||
VERILOG_SOURCES += ../../rtl/eth_mac_10g.v
|
||||
VERILOG_SOURCES += ../../rtl/axis_xgmii_rx_32.v
|
||||
VERILOG_SOURCES += ../../rtl/axis_xgmii_tx_32.v
|
||||
VERILOG_SOURCES += ../../rtl/axis_xgmii_rx_64.v
|
||||
VERILOG_SOURCES += ../../rtl/axis_xgmii_tx_64.v
|
||||
VERILOG_SOURCES += ../../rtl/lfsr.v
|
||||
VERILOG_SOURCES += ../../lib/axis/rtl/axis_adapter.v
|
||||
VERILOG_SOURCES += ../../lib/axis/rtl/axis_async_fifo.v
|
||||
VERILOG_SOURCES += ../../lib/axis/rtl/axis_async_fifo_adapter.v
|
||||
|
||||
# module parameters
|
||||
export PARAM_DATA_WIDTH ?= 64
|
||||
export PARAM_CTRL_WIDTH ?= $(shell expr $(PARAM_DATA_WIDTH) / 8 )
|
||||
export PARAM_AXIS_DATA_WIDTH ?= $(PARAM_DATA_WIDTH)
|
||||
export PARAM_AXIS_KEEP_ENABLE ?= $(shell expr $(PARAM_AXIS_DATA_WIDTH) \> 8 )
|
||||
export PARAM_AXIS_KEEP_WIDTH ?= $(shell expr $(PARAM_AXIS_DATA_WIDTH) / 8 )
|
||||
export PARAM_ENABLE_PADDING ?= 1
|
||||
export PARAM_ENABLE_DIC ?= 1
|
||||
export PARAM_MIN_FRAME_LENGTH ?= 64
|
||||
export PARAM_TX_FIFO_DEPTH ?= 16384
|
||||
export PARAM_TX_FIFO_PIPELINE_OUTPUT ?= 2
|
||||
export PARAM_TX_FRAME_FIFO ?= 1
|
||||
export PARAM_TX_DROP_BAD_FRAME ?= $(PARAM_TX_FRAME_FIFO)
|
||||
export PARAM_TX_DROP_WHEN_FULL ?= 0
|
||||
export PARAM_RX_FIFO_DEPTH ?= 16384
|
||||
export PARAM_RX_FIFO_PIPELINE_OUTPUT ?= 2
|
||||
export PARAM_RX_FRAME_FIFO ?= 1
|
||||
export PARAM_RX_DROP_BAD_FRAME ?= $(PARAM_RX_FRAME_FIFO)
|
||||
export PARAM_RX_DROP_WHEN_FULL ?= $(PARAM_RX_FRAME_FIFO)
|
||||
export PARAM_LOGIC_PTP_PERIOD_NS ?= 6
|
||||
export PARAM_LOGIC_PTP_PERIOD_FNS ?= 26214
|
||||
export PARAM_PTP_PERIOD_NS ?= 6
|
||||
export PARAM_PTP_PERIOD_FNS ?= 26214
|
||||
export PARAM_PTP_USE_SAMPLE_CLOCK ?= 0
|
||||
export PARAM_TX_PTP_TS_ENABLE ?= 0
|
||||
export PARAM_RX_PTP_TS_ENABLE ?= 0
|
||||
export PARAM_TX_PTP_TS_FIFO_DEPTH ?= 64
|
||||
export PARAM_RX_PTP_TS_FIFO_DEPTH ?= 64
|
||||
export PARAM_PTP_TS_WIDTH ?= 96
|
||||
export PARAM_TX_PTP_TAG_ENABLE ?= 0
|
||||
export PARAM_PTP_TAG_WIDTH ?= 16
|
||||
|
||||
SIM_BUILD ?= sim_build_$(MODULE)-$(PARAM_DATA_WIDTH)-$(PARAM_ENABLE_DIC)
|
||||
|
||||
ifeq ($(SIM), icarus)
|
||||
PLUSARGS += -fst
|
||||
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).DATA_WIDTH=$(PARAM_DATA_WIDTH)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).CTRL_WIDTH=$(PARAM_CTRL_WIDTH)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).AXIS_DATA_WIDTH=$(PARAM_AXIS_DATA_WIDTH)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).AXIS_KEEP_ENABLE=$(PARAM_AXIS_KEEP_ENABLE)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).AXIS_KEEP_WIDTH=$(PARAM_AXIS_KEEP_WIDTH)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).ENABLE_PADDING=$(PARAM_ENABLE_PADDING)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).ENABLE_DIC=$(PARAM_ENABLE_DIC)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).MIN_FRAME_LENGTH=$(PARAM_MIN_FRAME_LENGTH)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).TX_FIFO_DEPTH=$(PARAM_TX_FIFO_DEPTH)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).TX_FIFO_PIPELINE_OUTPUT=$(PARAM_TX_FIFO_PIPELINE_OUTPUT)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).TX_FRAME_FIFO=$(PARAM_TX_FRAME_FIFO)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).TX_DROP_BAD_FRAME=$(PARAM_TX_DROP_BAD_FRAME)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).TX_DROP_WHEN_FULL=$(PARAM_TX_DROP_WHEN_FULL)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).RX_FIFO_DEPTH=$(PARAM_RX_FIFO_DEPTH)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).RX_FIFO_PIPELINE_OUTPUT=$(PARAM_RX_FIFO_PIPELINE_OUTPUT)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).RX_FRAME_FIFO=$(PARAM_RX_FRAME_FIFO)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).RX_DROP_BAD_FRAME=$(PARAM_RX_DROP_BAD_FRAME)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).RX_DROP_WHEN_FULL=$(PARAM_RX_DROP_WHEN_FULL)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).LOGIC_PTP_PERIOD_NS=$(PARAM_LOGIC_PTP_PERIOD_NS)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).LOGIC_PTP_PERIOD_FNS=$(PARAM_LOGIC_PTP_PERIOD_FNS)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).PTP_PERIOD_NS=$(PARAM_PTP_PERIOD_NS)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).PTP_PERIOD_FNS=$(PARAM_PTP_PERIOD_FNS)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).PTP_USE_SAMPLE_CLOCK=$(PARAM_PTP_USE_SAMPLE_CLOCK)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).TX_PTP_TS_ENABLE=$(PARAM_TX_PTP_TS_ENABLE)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).RX_PTP_TS_ENABLE=$(PARAM_RX_PTP_TS_ENABLE)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).TX_PTP_TS_FIFO_DEPTH=$(PARAM_TX_PTP_TS_FIFO_DEPTH)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).RX_PTP_TS_FIFO_DEPTH=$(PARAM_RX_PTP_TS_FIFO_DEPTH)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).PTP_TS_WIDTH=$(PARAM_PTP_TS_WIDTH)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).TX_PTP_TAG_ENABLE=$(PARAM_TX_PTP_TAG_ENABLE)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).PTP_TAG_WIDTH=$(PARAM_PTP_TAG_WIDTH)
|
||||
|
||||
ifeq ($(WAVES), 1)
|
||||
VERILOG_SOURCES += iverilog_dump.v
|
||||
COMPILE_ARGS += -s iverilog_dump
|
||||
endif
|
||||
else ifeq ($(SIM), verilator)
|
||||
COMPILE_ARGS += -Wno-SELRANGE -Wno-WIDTH
|
||||
|
||||
COMPILE_ARGS += -GDATA_WIDTH=$(PARAM_DATA_WIDTH)
|
||||
COMPILE_ARGS += -GCTRL_WIDTH=$(PARAM_CTRL_WIDTH)
|
||||
COMPILE_ARGS += -GAXIS_DATA_WIDTH=$(PARAM_AXIS_DATA_WIDTH)
|
||||
COMPILE_ARGS += -GAXIS_KEEP_ENABLE=$(PARAM_AXIS_KEEP_ENABLE)
|
||||
COMPILE_ARGS += -GAXIS_KEEP_WIDTH=$(PARAM_AXIS_KEEP_WIDTH)
|
||||
COMPILE_ARGS += -GENABLE_PADDING=$(PARAM_ENABLE_PADDING)
|
||||
COMPILE_ARGS += -GENABLE_DIC=$(PARAM_ENABLE_DIC)
|
||||
COMPILE_ARGS += -GMIN_FRAME_LENGTH=$(PARAM_MIN_FRAME_LENGTH)
|
||||
COMPILE_ARGS += -GTX_FIFO_DEPTH=$(PARAM_TX_FIFO_DEPTH)
|
||||
COMPILE_ARGS += -GTX_FIFO_PIPELINE_OUTPUT=$(PARAM_TX_FIFO_PIPELINE_OUTPUT)
|
||||
COMPILE_ARGS += -GTX_FRAME_FIFO=$(PARAM_TX_FRAME_FIFO)
|
||||
COMPILE_ARGS += -GTX_DROP_BAD_FRAME=$(PARAM_TX_DROP_BAD_FRAME)
|
||||
COMPILE_ARGS += -GTX_DROP_WHEN_FULL=$(PARAM_TX_DROP_WHEN_FULL)
|
||||
COMPILE_ARGS += -GRX_FIFO_DEPTH=$(PARAM_RX_FIFO_DEPTH)
|
||||
COMPILE_ARGS += -GRX_FIFO_PIPELINE_OUTPUT=$(PARAM_RX_FIFO_PIPELINE_OUTPUT)
|
||||
COMPILE_ARGS += -GRX_FRAME_FIFO=$(PARAM_RX_FRAME_FIFO)
|
||||
COMPILE_ARGS += -GRX_DROP_BAD_FRAME=$(PARAM_RX_DROP_BAD_FRAME)
|
||||
COMPILE_ARGS += -GRX_DROP_WHEN_FULL=$(PARAM_RX_DROP_WHEN_FULL)
|
||||
COMPILE_ARGS += -GLOGIC_PTP_PERIOD_NS=$(PARAM_LOGIC_PTP_PERIOD_NS)
|
||||
COMPILE_ARGS += -GLOGIC_PTP_PERIOD_FNS=$(PARAM_LOGIC_PTP_PERIOD_FNS)
|
||||
COMPILE_ARGS += -GPTP_PERIOD_NS=$(PARAM_PTP_PERIOD_NS)
|
||||
COMPILE_ARGS += -GPTP_PERIOD_FNS=$(PARAM_PTP_PERIOD_FNS)
|
||||
COMPILE_ARGS += -GPTP_USE_SAMPLE_CLOCK=$(PARAM_PTP_USE_SAMPLE_CLOCK)
|
||||
COMPILE_ARGS += -GTX_PTP_TS_ENABLE=$(PARAM_TX_PTP_TS_ENABLE)
|
||||
COMPILE_ARGS += -GRX_PTP_TS_ENABLE=$(PARAM_RX_PTP_TS_ENABLE)
|
||||
COMPILE_ARGS += -GTX_PTP_TS_FIFO_DEPTH=$(PARAM_TX_PTP_TS_FIFO_DEPTH)
|
||||
COMPILE_ARGS += -GRX_PTP_TS_FIFO_DEPTH=$(PARAM_RX_PTP_TS_FIFO_DEPTH)
|
||||
COMPILE_ARGS += -GPTP_TS_WIDTH=$(PARAM_PTP_TS_WIDTH)
|
||||
COMPILE_ARGS += -GTX_PTP_TAG_ENABLE=$(PARAM_TX_PTP_TAG_ENABLE)
|
||||
COMPILE_ARGS += -GPTP_TAG_WIDTH=$(PARAM_PTP_TAG_WIDTH)
|
||||
|
||||
ifeq ($(WAVES), 1)
|
||||
COMPILE_ARGS += --trace-fst
|
||||
endif
|
||||
endif
|
||||
|
||||
iverilog_dump.v:
|
||||
echo 'module iverilog_dump();' > $@
|
||||
echo 'initial begin' >> $@
|
||||
echo ' $$dumpfile("$(TOPLEVEL).fst");' >> $@
|
||||
echo ' $$dumpvars(0, $(TOPLEVEL));' >> $@
|
||||
echo 'end' >> $@
|
||||
echo 'endmodule' >> $@
|
||||
|
||||
clean::
|
||||
@rm -rf sim_build_*
|
||||
@rm -rf iverilog_dump.v
|
||||
@rm -rf dump.fst $(TOPLEVEL).fst
|
||||
|
||||
include $(shell cocotb-config --makefiles)/Makefile.sim
|
||||
|
310
tb/eth_mac_10g_fifo/test_eth_mac_10g_fifo.py
Normal file
310
tb/eth_mac_10g_fifo/test_eth_mac_10g_fifo.py
Normal file
@ -0,0 +1,310 @@
|
||||
#!/usr/bin/env python
|
||||
"""
|
||||
|
||||
Copyright (c) 2020 Alex Forencich
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
"""
|
||||
|
||||
import itertools
|
||||
import logging
|
||||
import os
|
||||
|
||||
import pytest
|
||||
import cocotb_test.simulator
|
||||
|
||||
import cocotb
|
||||
from cocotb.clock import Clock
|
||||
from cocotb.triggers import RisingEdge
|
||||
from cocotb.regression import TestFactory
|
||||
|
||||
from cocotbext.eth import XgmiiFrame, XgmiiSource, XgmiiSink
|
||||
from cocotbext.axi import AxiStreamSource, AxiStreamSink
|
||||
|
||||
|
||||
class TB:
|
||||
def __init__(self, dut):
|
||||
self.dut = dut
|
||||
|
||||
self.log = logging.getLogger("cocotb.tb")
|
||||
self.log.setLevel(logging.DEBUG)
|
||||
|
||||
if len(dut.xgmii_txd) == 64:
|
||||
cocotb.fork(Clock(dut.logic_clk, 6.4, units="ns").start())
|
||||
cocotb.fork(Clock(dut.rx_clk, 6.4, units="ns").start())
|
||||
cocotb.fork(Clock(dut.tx_clk, 6.4, units="ns").start())
|
||||
else:
|
||||
cocotb.fork(Clock(dut.logic_clk, 3.2, units="ns").start())
|
||||
cocotb.fork(Clock(dut.rx_clk, 3.2, units="ns").start())
|
||||
cocotb.fork(Clock(dut.tx_clk, 3.2, units="ns").start())
|
||||
|
||||
self.xgmii_source = XgmiiSource(dut.xgmii_rxd, dut.xgmii_rxc, dut.rx_clk, dut.rx_rst)
|
||||
self.xgmii_sink = XgmiiSink(dut.xgmii_txd, dut.xgmii_txc, dut.tx_clk, dut.tx_rst)
|
||||
|
||||
self.axis_source = AxiStreamSource(dut, "tx_axis", dut.logic_clk, dut.logic_rst)
|
||||
self.axis_sink = AxiStreamSink(dut, "rx_axis", dut.logic_clk, dut.logic_rst)
|
||||
|
||||
dut.ptp_sample_clk.setimmediatevalue(0)
|
||||
dut.ptp_ts_96.setimmediatevalue(0)
|
||||
|
||||
async def reset(self):
|
||||
self.dut.logic_rst.setimmediatevalue(0)
|
||||
self.dut.rx_rst.setimmediatevalue(0)
|
||||
self.dut.tx_rst.setimmediatevalue(0)
|
||||
await RisingEdge(self.dut.logic_clk)
|
||||
await RisingEdge(self.dut.logic_clk)
|
||||
self.dut.logic_rst <= 1
|
||||
self.dut.rx_rst <= 1
|
||||
self.dut.tx_rst <= 1
|
||||
await RisingEdge(self.dut.logic_clk)
|
||||
await RisingEdge(self.dut.logic_clk)
|
||||
self.dut.logic_rst <= 0
|
||||
self.dut.rx_rst <= 0
|
||||
self.dut.tx_rst <= 0
|
||||
await RisingEdge(self.dut.logic_clk)
|
||||
await RisingEdge(self.dut.logic_clk)
|
||||
|
||||
|
||||
async def run_test_rx(dut, payload_lengths=None, payload_data=None, ifg=12):
|
||||
|
||||
tb = TB(dut)
|
||||
|
||||
tb.xgmii_source.ifg = ifg
|
||||
tb.dut.ifg_delay <= ifg
|
||||
|
||||
await tb.reset()
|
||||
|
||||
test_frames = [payload_data(x) for x in payload_lengths()]
|
||||
|
||||
for test_data in test_frames:
|
||||
test_frame = XgmiiFrame.from_payload(test_data)
|
||||
await tb.xgmii_source.send(test_frame)
|
||||
|
||||
for test_data in test_frames:
|
||||
rx_frame = await tb.axis_sink.recv()
|
||||
|
||||
assert rx_frame.tdata == test_data
|
||||
assert rx_frame.tuser == 0
|
||||
|
||||
assert tb.axis_sink.empty()
|
||||
|
||||
await RisingEdge(dut.logic_clk)
|
||||
await RisingEdge(dut.logic_clk)
|
||||
|
||||
|
||||
async def run_test_tx(dut, payload_lengths=None, payload_data=None, ifg=12):
|
||||
|
||||
tb = TB(dut)
|
||||
|
||||
tb.xgmii_source.ifg = ifg
|
||||
tb.dut.ifg_delay <= ifg
|
||||
|
||||
await tb.reset()
|
||||
|
||||
test_frames = [payload_data(x) for x in payload_lengths()]
|
||||
|
||||
for test_data in test_frames:
|
||||
await tb.axis_source.send(test_data)
|
||||
|
||||
for test_data in test_frames:
|
||||
rx_frame = await tb.xgmii_sink.recv()
|
||||
|
||||
assert rx_frame.get_payload() == test_data
|
||||
assert rx_frame.check_fcs()
|
||||
|
||||
assert tb.xgmii_sink.empty()
|
||||
|
||||
await RisingEdge(dut.logic_clk)
|
||||
await RisingEdge(dut.logic_clk)
|
||||
|
||||
|
||||
async def run_test_tx_alignment(dut, payload_data=None, ifg=12):
|
||||
|
||||
enable_dic = int(os.getenv("PARAM_ENABLE_DIC"))
|
||||
|
||||
tb = TB(dut)
|
||||
|
||||
byte_width = tb.axis_source.width // 8
|
||||
|
||||
tb.xgmii_source.ifg = ifg
|
||||
tb.dut.ifg_delay <= ifg
|
||||
|
||||
for length in range(60, 92):
|
||||
|
||||
await tb.reset()
|
||||
|
||||
test_frames = [payload_data(length) for k in range(10)]
|
||||
start_lane = []
|
||||
|
||||
for test_data in test_frames:
|
||||
await tb.axis_source.send(test_data)
|
||||
|
||||
for test_data in test_frames:
|
||||
rx_frame = await tb.xgmii_sink.recv()
|
||||
|
||||
assert rx_frame.get_payload() == test_data
|
||||
assert rx_frame.check_fcs()
|
||||
assert rx_frame.ctrl is None
|
||||
|
||||
start_lane.append(rx_frame.rx_start_lane)
|
||||
|
||||
tb.log.info("length: %d", length)
|
||||
tb.log.info("start_lane: %s", start_lane)
|
||||
|
||||
start_lane_ref = []
|
||||
|
||||
# compute expected starting lanes
|
||||
lane = 0
|
||||
deficit_idle_count = 0
|
||||
|
||||
for test_data in test_frames:
|
||||
if ifg == 0:
|
||||
lane = 0
|
||||
|
||||
start_lane_ref.append(lane)
|
||||
lane = (lane + len(test_data)+4+ifg) % byte_width
|
||||
|
||||
if enable_dic:
|
||||
offset = lane % 4
|
||||
if deficit_idle_count+offset >= 4:
|
||||
offset += 4
|
||||
lane = (lane - offset) % byte_width
|
||||
deficit_idle_count = (deficit_idle_count + offset) % 4
|
||||
else:
|
||||
offset = lane % 4
|
||||
if offset > 0:
|
||||
offset += 4
|
||||
lane = (lane - offset) % byte_width
|
||||
|
||||
tb.log.info("start_lane_ref: %s", start_lane_ref)
|
||||
|
||||
assert start_lane_ref == start_lane
|
||||
|
||||
await RisingEdge(dut.logic_clk)
|
||||
|
||||
assert tb.xgmii_sink.empty()
|
||||
|
||||
await RisingEdge(dut.logic_clk)
|
||||
await RisingEdge(dut.logic_clk)
|
||||
|
||||
|
||||
def size_list():
|
||||
return list(range(60, 128)) + [512, 1514, 9214] + [60]*10
|
||||
|
||||
|
||||
def incrementing_payload(length):
|
||||
return bytearray(itertools.islice(itertools.cycle(range(256)), length))
|
||||
|
||||
|
||||
def cycle_en():
|
||||
return itertools.cycle([0, 0, 0, 1])
|
||||
|
||||
|
||||
if cocotb.SIM_NAME:
|
||||
|
||||
for test in [run_test_rx, run_test_tx]:
|
||||
|
||||
factory = TestFactory(test)
|
||||
factory.add_option("payload_lengths", [size_list])
|
||||
factory.add_option("payload_data", [incrementing_payload])
|
||||
factory.add_option("ifg", [12, 0])
|
||||
factory.generate_tests()
|
||||
|
||||
factory = TestFactory(run_test_tx_alignment)
|
||||
factory.add_option("payload_data", [incrementing_payload])
|
||||
factory.add_option("ifg", [12])
|
||||
factory.generate_tests()
|
||||
|
||||
|
||||
# cocotb-test
|
||||
|
||||
tests_dir = os.path.abspath(os.path.dirname(__file__))
|
||||
rtl_dir = os.path.abspath(os.path.join(tests_dir, '..', '..', 'rtl'))
|
||||
lib_dir = os.path.abspath(os.path.join(rtl_dir, '..', 'lib'))
|
||||
axis_rtl_dir = os.path.abspath(os.path.join(lib_dir, 'axis', 'rtl'))
|
||||
|
||||
|
||||
@pytest.mark.parametrize("enable_dic", [1, 0])
|
||||
@pytest.mark.parametrize("data_width", [32, 64])
|
||||
def test_eth_mac_10g_fifo(request, data_width, enable_dic):
|
||||
dut = "eth_mac_10g_fifo"
|
||||
module = os.path.splitext(os.path.basename(__file__))[0]
|
||||
toplevel = dut
|
||||
|
||||
verilog_sources = [
|
||||
os.path.join(rtl_dir, f"{dut}.v"),
|
||||
os.path.join(rtl_dir, "eth_mac_10g.v"),
|
||||
os.path.join(rtl_dir, "axis_xgmii_rx_32.v"),
|
||||
os.path.join(rtl_dir, "axis_xgmii_rx_64.v"),
|
||||
os.path.join(rtl_dir, "axis_xgmii_tx_32.v"),
|
||||
os.path.join(rtl_dir, "axis_xgmii_tx_64.v"),
|
||||
os.path.join(rtl_dir, "lfsr.v"),
|
||||
os.path.join(axis_rtl_dir, "axis_adapter.v"),
|
||||
os.path.join(axis_rtl_dir, "axis_async_fifo.v"),
|
||||
os.path.join(axis_rtl_dir, "axis_async_fifo_adapter.v"),
|
||||
]
|
||||
|
||||
parameters = {}
|
||||
|
||||
parameters['DATA_WIDTH'] = data_width
|
||||
parameters['CTRL_WIDTH'] = parameters['DATA_WIDTH'] // 8
|
||||
parameters['AXIS_DATA_WIDTH'] = parameters['DATA_WIDTH']
|
||||
parameters['AXIS_KEEP_ENABLE'] = int(parameters['AXIS_DATA_WIDTH'] > 8)
|
||||
parameters['AXIS_KEEP_WIDTH'] = parameters['AXIS_DATA_WIDTH'] // 8
|
||||
parameters['ENABLE_PADDING'] = 1
|
||||
parameters['ENABLE_DIC'] = enable_dic
|
||||
parameters['MIN_FRAME_LENGTH'] = 64
|
||||
parameters['TX_FIFO_DEPTH'] = 16384
|
||||
parameters['TX_FIFO_PIPELINE_OUTPUT'] = 2
|
||||
parameters['TX_FRAME_FIFO'] = 1
|
||||
parameters['TX_DROP_BAD_FRAME'] = parameters['TX_FRAME_FIFO']
|
||||
parameters['TX_DROP_WHEN_FULL'] = 0
|
||||
parameters['RX_FIFO_DEPTH'] = 16384
|
||||
parameters['RX_FIFO_PIPELINE_OUTPUT'] = 2
|
||||
parameters['RX_FRAME_FIFO'] = 1
|
||||
parameters['RX_DROP_BAD_FRAME'] = parameters['RX_FRAME_FIFO']
|
||||
parameters['RX_DROP_WHEN_FULL'] = parameters['RX_FRAME_FIFO']
|
||||
parameters['LOGIC_PTP_PERIOD_NS'] = 0x6 if parameters['AXIS_DATA_WIDTH'] == 64 else 0x3
|
||||
parameters['LOGIC_PTP_PERIOD_FNS'] = 0x6666 if parameters['AXIS_DATA_WIDTH'] == 64 else 0x3333
|
||||
parameters['PTP_PERIOD_NS'] = 0x6 if parameters['DATA_WIDTH'] == 64 else 0x3
|
||||
parameters['PTP_PERIOD_FNS'] = 0x6666 if parameters['DATA_WIDTH'] == 64 else 0x3333
|
||||
parameters['PTP_USE_SAMPLE_CLOCK'] = 0
|
||||
parameters['TX_PTP_TS_ENABLE'] = 0
|
||||
parameters['RX_PTP_TS_ENABLE'] = 0
|
||||
parameters['TX_PTP_TS_FIFO_DEPTH'] = 64
|
||||
parameters['RX_PTP_TS_FIFO_DEPTH'] = 64
|
||||
parameters['PTP_TS_WIDTH'] = 96
|
||||
parameters['TX_PTP_TAG_ENABLE'] = 0
|
||||
parameters['PTP_TAG_WIDTH'] = 16
|
||||
|
||||
extra_env = {f'PARAM_{k}': str(v) for k, v in parameters.items()}
|
||||
|
||||
sim_build = os.path.join(tests_dir,
|
||||
"sim_build_"+request.node.name.replace('[', '-').replace(']', ''))
|
||||
|
||||
cocotb_test.simulator.run(
|
||||
python_search=[tests_dir],
|
||||
verilog_sources=verilog_sources,
|
||||
toplevel=toplevel,
|
||||
module=module,
|
||||
parameters=parameters,
|
||||
sim_build=sim_build,
|
||||
extra_env=extra_env,
|
||||
)
|
107
tb/eth_mac_1g/Makefile
Normal file
107
tb/eth_mac_1g/Makefile
Normal file
@ -0,0 +1,107 @@
|
||||
# Copyright (c) 2020 Alex Forencich
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
|
||||
TOPLEVEL_LANG = verilog
|
||||
|
||||
SIM ?= icarus
|
||||
WAVES ?= 0
|
||||
|
||||
COCOTB_HDL_TIMEUNIT = 1ns
|
||||
COCOTB_HDL_TIMEPRECISION = 1ps
|
||||
|
||||
DUT = eth_mac_1g
|
||||
TOPLEVEL = $(DUT)
|
||||
MODULE = test_$(DUT)
|
||||
VERILOG_SOURCES += ../../rtl/$(DUT).v
|
||||
VERILOG_SOURCES += ../../rtl/axis_gmii_rx.v
|
||||
VERILOG_SOURCES += ../../rtl/axis_gmii_tx.v
|
||||
VERILOG_SOURCES += ../../rtl/lfsr.v
|
||||
|
||||
# module parameters
|
||||
export PARAM_DATA_WIDTH ?= 8
|
||||
export PARAM_ENABLE_PADDING ?= 1
|
||||
export PARAM_MIN_FRAME_LENGTH ?= 64
|
||||
export PARAM_TX_PTP_TS_ENABLE ?= 0
|
||||
export PARAM_TX_PTP_TS_WIDTH ?= 96
|
||||
export PARAM_TX_PTP_TAG_ENABLE ?= $(PARAM_TX_PTP_TS_ENABLE)
|
||||
export PARAM_TX_PTP_TAG_WIDTH ?= 16
|
||||
export PARAM_RX_PTP_TS_ENABLE ?= 0
|
||||
export PARAM_RX_PTP_TS_WIDTH ?= 96
|
||||
# export PARAM_TX_USER_WIDTH ?= (TX_PTP_TAG_WIDTH if TX_PTP_TAG_ENABLE else 0) + 1
|
||||
export PARAM_TX_USER_WIDTH ?= 1
|
||||
# export PARAM_RX_USER_WIDTH ?= (RX_PTP_TS_WIDTH if RX_PTP_TS_ENABLE else 0) + 1
|
||||
export PARAM_RX_USER_WIDTH ?= 1
|
||||
|
||||
SIM_BUILD ?= sim_build_$(MODULE)
|
||||
|
||||
ifeq ($(SIM), icarus)
|
||||
PLUSARGS += -fst
|
||||
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).DATA_WIDTH=$(PARAM_DATA_WIDTH)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).ENABLE_PADDING=$(PARAM_ENABLE_PADDING)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).MIN_FRAME_LENGTH=$(PARAM_MIN_FRAME_LENGTH)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).TX_PTP_TS_ENABLE=$(PARAM_TX_PTP_TS_ENABLE)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).TX_PTP_TS_WIDTH=$(PARAM_TX_PTP_TS_WIDTH)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).TX_PTP_TAG_ENABLE=$(PARAM_TX_PTP_TAG_ENABLE)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).TX_PTP_TAG_WIDTH=$(PARAM_TX_PTP_TAG_WIDTH)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).RX_PTP_TS_ENABLE=$(PARAM_RX_PTP_TS_ENABLE)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).RX_PTP_TS_WIDTH=$(PARAM_RX_PTP_TS_WIDTH)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).TX_USER_WIDTH=$(PARAM_TX_USER_WIDTH)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).RX_USER_WIDTH=$(PARAM_RX_USER_WIDTH)
|
||||
|
||||
ifeq ($(WAVES), 1)
|
||||
VERILOG_SOURCES += iverilog_dump.v
|
||||
COMPILE_ARGS += -s iverilog_dump
|
||||
endif
|
||||
else ifeq ($(SIM), verilator)
|
||||
COMPILE_ARGS += -Wno-SELRANGE -Wno-WIDTH
|
||||
|
||||
COMPILE_ARGS += -GDATA_WIDTH=$(PARAM_DATA_WIDTH)
|
||||
COMPILE_ARGS += -GENABLE_PADDING=$(PARAM_ENABLE_PADDING)
|
||||
COMPILE_ARGS += -GMIN_FRAME_LENGTH=$(PARAM_MIN_FRAME_LENGTH)
|
||||
COMPILE_ARGS += -GTX_PTP_TS_ENABLE=$(PARAM_TX_PTP_TS_ENABLE)
|
||||
COMPILE_ARGS += -GTX_PTP_TS_WIDTH=$(PARAM_TX_PTP_TS_WIDTH)
|
||||
COMPILE_ARGS += -GTX_PTP_TAG_ENABLE=$(PARAM_TX_PTP_TAG_ENABLE)
|
||||
COMPILE_ARGS += -GTX_PTP_TAG_WIDTH=$(PARAM_TX_PTP_TAG_WIDTH)
|
||||
COMPILE_ARGS += -GRX_PTP_TS_ENABLE=$(PARAM_RX_PTP_TS_ENABLE)
|
||||
COMPILE_ARGS += -GRX_PTP_TS_WIDTH=$(PARAM_RX_PTP_TS_WIDTH)
|
||||
COMPILE_ARGS += -GTX_USER_WIDTH=$(PARAM_TX_USER_WIDTH)
|
||||
COMPILE_ARGS += -GRX_USER_WIDTH=$(PARAM_RX_USER_WIDTH)
|
||||
|
||||
ifeq ($(WAVES), 1)
|
||||
COMPILE_ARGS += --trace-fst
|
||||
endif
|
||||
endif
|
||||
|
||||
iverilog_dump.v:
|
||||
echo 'module iverilog_dump();' > $@
|
||||
echo 'initial begin' >> $@
|
||||
echo ' $$dumpfile("$(TOPLEVEL).fst");' >> $@
|
||||
echo ' $$dumpvars(0, $(TOPLEVEL));' >> $@
|
||||
echo 'end' >> $@
|
||||
echo 'endmodule' >> $@
|
||||
|
||||
clean::
|
||||
@rm -rf sim_build_*
|
||||
@rm -rf iverilog_dump.v
|
||||
@rm -rf dump.fst $(TOPLEVEL).fst
|
||||
|
||||
include $(shell cocotb-config --makefiles)/Makefile.sim
|
||||
|
261
tb/eth_mac_1g/test_eth_mac_1g.py
Normal file
261
tb/eth_mac_1g/test_eth_mac_1g.py
Normal file
@ -0,0 +1,261 @@
|
||||
#!/usr/bin/env python
|
||||
"""
|
||||
|
||||
Copyright (c) 2020 Alex Forencich
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
"""
|
||||
|
||||
import itertools
|
||||
import logging
|
||||
import os
|
||||
|
||||
import cocotb_test.simulator
|
||||
|
||||
import cocotb
|
||||
from cocotb.clock import Clock
|
||||
from cocotb.triggers import RisingEdge
|
||||
from cocotb.regression import TestFactory
|
||||
|
||||
from cocotbext.eth import GmiiFrame, GmiiSource, GmiiSink
|
||||
from cocotbext.axi import AxiStreamSource, AxiStreamSink
|
||||
|
||||
|
||||
class TB:
|
||||
def __init__(self, dut):
|
||||
self.dut = dut
|
||||
|
||||
self.log = logging.getLogger("cocotb.tb")
|
||||
self.log.setLevel(logging.DEBUG)
|
||||
|
||||
self._enable_generator_rx = None
|
||||
self._enable_generator_tx = None
|
||||
self._enable_cr_rx = None
|
||||
self._enable_cr_tx = None
|
||||
|
||||
cocotb.fork(Clock(dut.rx_clk, 8, units="ns").start())
|
||||
cocotb.fork(Clock(dut.tx_clk, 8, units="ns").start())
|
||||
|
||||
self.gmii_source = GmiiSource(dut.gmii_rxd, dut.gmii_rx_er, dut.gmii_rx_dv,
|
||||
dut.rx_clk, dut.rx_rst, dut.rx_clk_enable, dut.rx_mii_select)
|
||||
self.gmii_sink = GmiiSink(dut.gmii_txd, dut.gmii_tx_er, dut.gmii_tx_en,
|
||||
dut.tx_clk, dut.tx_rst, dut.tx_clk_enable, dut.tx_mii_select)
|
||||
|
||||
self.axis_source = AxiStreamSource(dut, "tx_axis", dut.tx_clk, dut.tx_rst)
|
||||
self.axis_sink = AxiStreamSink(dut, "rx_axis", dut.rx_clk, dut.rx_rst)
|
||||
|
||||
dut.rx_clk_enable.setimmediatevalue(1)
|
||||
dut.tx_clk_enable.setimmediatevalue(1)
|
||||
dut.rx_mii_select.setimmediatevalue(0)
|
||||
dut.tx_mii_select.setimmediatevalue(0)
|
||||
dut.rx_ptp_ts.setimmediatevalue(0)
|
||||
dut.tx_ptp_ts.setimmediatevalue(0)
|
||||
dut.ifg_delay.setimmediatevalue(0)
|
||||
|
||||
async def reset(self):
|
||||
self.dut.rx_rst.setimmediatevalue(0)
|
||||
self.dut.tx_rst.setimmediatevalue(0)
|
||||
await RisingEdge(self.dut.tx_clk)
|
||||
await RisingEdge(self.dut.tx_clk)
|
||||
self.dut.rx_rst <= 1
|
||||
self.dut.tx_rst <= 1
|
||||
await RisingEdge(self.dut.tx_clk)
|
||||
await RisingEdge(self.dut.tx_clk)
|
||||
self.dut.rx_rst <= 0
|
||||
self.dut.tx_rst <= 0
|
||||
await RisingEdge(self.dut.tx_clk)
|
||||
await RisingEdge(self.dut.tx_clk)
|
||||
|
||||
def set_enable_generator_rx(self, generator=None):
|
||||
if self._enable_cr_rx is not None:
|
||||
self._enable_cr_rx.kill()
|
||||
self._enable_cr_rx = None
|
||||
|
||||
self._enable_generator_rx = generator
|
||||
|
||||
if self._enable_generator_rx is not None:
|
||||
self._enable_cr_rx = cocotb.fork(self._run_enable_rx())
|
||||
|
||||
def set_enable_generator_tx(self, generator=None):
|
||||
if self._enable_cr_tx is not None:
|
||||
self._enable_cr_tx.kill()
|
||||
self._enable_cr_tx = None
|
||||
|
||||
self._enable_generator_tx = generator
|
||||
|
||||
if self._enable_generator_tx is not None:
|
||||
self._enable_cr_tx = cocotb.fork(self._run_enable_tx())
|
||||
|
||||
def clear_enable_generator_rx(self):
|
||||
self.set_enable_generator_rx(None)
|
||||
|
||||
def clear_enable_generator_tx(self):
|
||||
self.set_enable_generator_tx(None)
|
||||
|
||||
async def _run_enable_rx(self):
|
||||
for val in self._enable_generator_rx:
|
||||
self.dut.rx_clk_enable <= val
|
||||
await RisingEdge(self.dut.rx_clk)
|
||||
|
||||
async def _run_enable_tx(self):
|
||||
for val in self._enable_generator_tx:
|
||||
self.dut.tx_clk_enable <= val
|
||||
await RisingEdge(self.dut.tx_clk)
|
||||
|
||||
|
||||
async def run_test_rx(dut, payload_lengths=None, payload_data=None, ifg=12, enable_gen=None, mii_sel=False):
|
||||
|
||||
tb = TB(dut)
|
||||
|
||||
tb.gmii_source.ifg = ifg
|
||||
tb.dut.ifg_delay <= ifg
|
||||
tb.dut.rx_mii_select <= mii_sel
|
||||
tb.dut.tx_mii_select <= mii_sel
|
||||
|
||||
if enable_gen is not None:
|
||||
tb.set_enable_generator_rx(enable_gen())
|
||||
tb.set_enable_generator_tx(enable_gen())
|
||||
|
||||
await tb.reset()
|
||||
|
||||
test_frames = [payload_data(x) for x in payload_lengths()]
|
||||
|
||||
for test_data in test_frames:
|
||||
test_frame = GmiiFrame.from_payload(test_data)
|
||||
await tb.gmii_source.send(test_frame)
|
||||
|
||||
for test_data in test_frames:
|
||||
rx_frame = await tb.axis_sink.recv()
|
||||
|
||||
assert rx_frame.tdata == test_data
|
||||
assert rx_frame.tuser == 0
|
||||
|
||||
assert tb.axis_sink.empty()
|
||||
|
||||
await RisingEdge(dut.rx_clk)
|
||||
await RisingEdge(dut.rx_clk)
|
||||
|
||||
|
||||
async def run_test_tx(dut, payload_lengths=None, payload_data=None, ifg=12, enable_gen=None, mii_sel=False):
|
||||
|
||||
tb = TB(dut)
|
||||
|
||||
tb.gmii_source.ifg = ifg
|
||||
tb.dut.ifg_delay <= ifg
|
||||
tb.dut.rx_mii_select <= mii_sel
|
||||
tb.dut.tx_mii_select <= mii_sel
|
||||
|
||||
if enable_gen is not None:
|
||||
tb.set_enable_generator_rx(enable_gen())
|
||||
tb.set_enable_generator_tx(enable_gen())
|
||||
|
||||
await tb.reset()
|
||||
|
||||
test_frames = [payload_data(x) for x in payload_lengths()]
|
||||
|
||||
for test_data in test_frames:
|
||||
await tb.axis_source.send(test_data)
|
||||
|
||||
for test_data in test_frames:
|
||||
rx_frame = await tb.gmii_sink.recv()
|
||||
|
||||
assert rx_frame.get_payload() == test_data
|
||||
assert rx_frame.check_fcs()
|
||||
assert rx_frame.error is None
|
||||
|
||||
assert tb.gmii_sink.empty()
|
||||
|
||||
await RisingEdge(dut.tx_clk)
|
||||
await RisingEdge(dut.tx_clk)
|
||||
|
||||
|
||||
def size_list():
|
||||
return list(range(60, 128)) + [512, 1514] + [60]*10
|
||||
|
||||
|
||||
def incrementing_payload(length):
|
||||
return bytearray(itertools.islice(itertools.cycle(range(256)), length))
|
||||
|
||||
|
||||
def cycle_en():
|
||||
return itertools.cycle([0, 0, 0, 1])
|
||||
|
||||
|
||||
if cocotb.SIM_NAME:
|
||||
|
||||
for test in [run_test_rx, run_test_tx]:
|
||||
|
||||
factory = TestFactory(test)
|
||||
factory.add_option("payload_lengths", [size_list])
|
||||
factory.add_option("payload_data", [incrementing_payload])
|
||||
factory.add_option("ifg", [12])
|
||||
factory.add_option("enable_gen", [None, cycle_en])
|
||||
factory.add_option("mii_sel", [False, True])
|
||||
factory.generate_tests()
|
||||
|
||||
|
||||
# cocotb-test
|
||||
|
||||
tests_dir = os.path.abspath(os.path.dirname(__file__))
|
||||
rtl_dir = os.path.abspath(os.path.join(tests_dir, '..', '..', 'rtl'))
|
||||
lib_dir = os.path.abspath(os.path.join(rtl_dir, '..', 'lib'))
|
||||
axis_rtl_dir = os.path.abspath(os.path.join(lib_dir, 'axis', 'rtl'))
|
||||
|
||||
|
||||
def test_eth_mac_1g(request):
|
||||
dut = "eth_mac_1g"
|
||||
module = os.path.splitext(os.path.basename(__file__))[0]
|
||||
toplevel = dut
|
||||
|
||||
verilog_sources = [
|
||||
os.path.join(rtl_dir, f"{dut}.v"),
|
||||
os.path.join(rtl_dir, "axis_gmii_rx.v"),
|
||||
os.path.join(rtl_dir, "axis_gmii_tx.v"),
|
||||
os.path.join(rtl_dir, "lfsr.v"),
|
||||
]
|
||||
|
||||
parameters = {}
|
||||
|
||||
parameters['DATA_WIDTH'] = 8
|
||||
parameters['ENABLE_PADDING'] = 1
|
||||
parameters['MIN_FRAME_LENGTH'] = 64
|
||||
parameters['TX_PTP_TS_ENABLE'] = 0
|
||||
parameters['TX_PTP_TS_WIDTH'] = 96
|
||||
parameters['TX_PTP_TAG_ENABLE'] = parameters['TX_PTP_TS_ENABLE']
|
||||
parameters['TX_PTP_TAG_WIDTH'] = 16
|
||||
parameters['RX_PTP_TS_ENABLE'] = 0
|
||||
parameters['RX_PTP_TS_WIDTH'] = 96
|
||||
parameters['TX_USER_WIDTH'] = (parameters['TX_PTP_TAG_WIDTH'] if parameters['TX_PTP_TAG_ENABLE'] else 0) + 1
|
||||
parameters['RX_USER_WIDTH'] = (parameters['RX_PTP_TS_WIDTH'] if parameters['RX_PTP_TS_ENABLE'] else 0) + 1
|
||||
|
||||
extra_env = {f'PARAM_{k}': str(v) for k, v in parameters.items()}
|
||||
|
||||
sim_build = os.path.join(tests_dir,
|
||||
"sim_build_"+request.node.name.replace('[', '-').replace(']', ''))
|
||||
|
||||
cocotb_test.simulator.run(
|
||||
python_search=[tests_dir],
|
||||
verilog_sources=verilog_sources,
|
||||
toplevel=toplevel,
|
||||
module=module,
|
||||
parameters=parameters,
|
||||
sim_build=sim_build,
|
||||
extra_env=extra_env,
|
||||
)
|
115
tb/eth_mac_1g_fifo/Makefile
Normal file
115
tb/eth_mac_1g_fifo/Makefile
Normal file
@ -0,0 +1,115 @@
|
||||
# Copyright (c) 2020 Alex Forencich
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
|
||||
TOPLEVEL_LANG = verilog
|
||||
|
||||
SIM ?= icarus
|
||||
WAVES ?= 0
|
||||
|
||||
COCOTB_HDL_TIMEUNIT = 1ns
|
||||
COCOTB_HDL_TIMEPRECISION = 1ps
|
||||
|
||||
DUT = eth_mac_1g_fifo
|
||||
TOPLEVEL = $(DUT)
|
||||
MODULE = test_$(DUT)
|
||||
VERILOG_SOURCES += ../../rtl/$(DUT).v
|
||||
VERILOG_SOURCES += ../../rtl/eth_mac_1g.v
|
||||
VERILOG_SOURCES += ../../rtl/axis_gmii_rx.v
|
||||
VERILOG_SOURCES += ../../rtl/axis_gmii_tx.v
|
||||
VERILOG_SOURCES += ../../rtl/lfsr.v
|
||||
VERILOG_SOURCES += ../../lib/axis/rtl/axis_adapter.v
|
||||
VERILOG_SOURCES += ../../lib/axis/rtl/axis_async_fifo.v
|
||||
VERILOG_SOURCES += ../../lib/axis/rtl/axis_async_fifo_adapter.v
|
||||
|
||||
# module parameters
|
||||
export PARAM_AXIS_DATA_WIDTH ?= 8
|
||||
export PARAM_AXIS_KEEP_ENABLE ?= $(shell expr $(PARAM_AXIS_DATA_WIDTH) \> 8 )
|
||||
export PARAM_AXIS_KEEP_WIDTH ?= $(shell expr $(PARAM_AXIS_DATA_WIDTH) / 8 )
|
||||
export PARAM_ENABLE_PADDING ?= 1
|
||||
export PARAM_MIN_FRAME_LENGTH ?= 64
|
||||
export PARAM_TX_FIFO_DEPTH ?= 16384
|
||||
export PARAM_TX_FRAME_FIFO ?= 1
|
||||
export PARAM_TX_DROP_BAD_FRAME ?= $(PARAM_TX_FRAME_FIFO)
|
||||
export PARAM_TX_DROP_WHEN_FULL ?= 0
|
||||
export PARAM_RX_FIFO_DEPTH ?= 16384
|
||||
export PARAM_RX_FRAME_FIFO ?= 1
|
||||
export PARAM_RX_DROP_BAD_FRAME ?= $(PARAM_RX_FRAME_FIFO)
|
||||
export PARAM_RX_DROP_WHEN_FULL ?= $(PARAM_RX_FRAME_FIFO)
|
||||
|
||||
SIM_BUILD ?= sim_build_$(MODULE)
|
||||
|
||||
ifeq ($(SIM), icarus)
|
||||
PLUSARGS += -fst
|
||||
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).AXIS_DATA_WIDTH=$(PARAM_AXIS_DATA_WIDTH)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).AXIS_KEEP_ENABLE=$(PARAM_AXIS_KEEP_ENABLE)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).AXIS_KEEP_WIDTH=$(PARAM_AXIS_KEEP_WIDTH)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).ENABLE_PADDING=$(PARAM_ENABLE_PADDING)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).MIN_FRAME_LENGTH=$(PARAM_MIN_FRAME_LENGTH)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).TX_FIFO_DEPTH=$(PARAM_TX_FIFO_DEPTH)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).TX_FRAME_FIFO=$(PARAM_TX_FRAME_FIFO)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).TX_DROP_BAD_FRAME=$(PARAM_TX_DROP_BAD_FRAME)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).TX_DROP_WHEN_FULL=$(PARAM_TX_DROP_WHEN_FULL)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).RX_FIFO_DEPTH=$(PARAM_RX_FIFO_DEPTH)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).RX_FRAME_FIFO=$(PARAM_RX_FRAME_FIFO)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).RX_DROP_BAD_FRAME=$(PARAM_RX_DROP_BAD_FRAME)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).RX_DROP_WHEN_FULL=$(PARAM_RX_DROP_WHEN_FULL)
|
||||
|
||||
ifeq ($(WAVES), 1)
|
||||
VERILOG_SOURCES += iverilog_dump.v
|
||||
COMPILE_ARGS += -s iverilog_dump
|
||||
endif
|
||||
else ifeq ($(SIM), verilator)
|
||||
COMPILE_ARGS += -Wno-SELRANGE -Wno-WIDTH
|
||||
|
||||
COMPILE_ARGS += -GAXIS_DATA_WIDTH=$(PARAM_AXIS_DATA_WIDTH)
|
||||
COMPILE_ARGS += -GAXIS_KEEP_ENABLE=$(PARAM_AXIS_KEEP_ENABLE)
|
||||
COMPILE_ARGS += -GAXIS_KEEP_WIDTH=$(PARAM_AXIS_KEEP_WIDTH)
|
||||
COMPILE_ARGS += -GENABLE_PADDING=$(PARAM_ENABLE_PADDING)
|
||||
COMPILE_ARGS += -GMIN_FRAME_LENGTH=$(PARAM_MIN_FRAME_LENGTH)
|
||||
COMPILE_ARGS += -GTX_FIFO_DEPTH=$(PARAM_TX_FIFO_DEPTH)
|
||||
COMPILE_ARGS += -GTX_FRAME_FIFO=$(PARAM_TX_FRAME_FIFO)
|
||||
COMPILE_ARGS += -GTX_DROP_BAD_FRAME=$(PARAM_TX_DROP_BAD_FRAME)
|
||||
COMPILE_ARGS += -GTX_DROP_WHEN_FULL=$(PARAM_TX_DROP_WHEN_FULL)
|
||||
COMPILE_ARGS += -GRX_FIFO_DEPTH=$(PARAM_RX_FIFO_DEPTH)
|
||||
COMPILE_ARGS += -GRX_FRAME_FIFO=$(PARAM_RX_FRAME_FIFO)
|
||||
COMPILE_ARGS += -GRX_DROP_BAD_FRAME=$(PARAM_RX_DROP_BAD_FRAME)
|
||||
COMPILE_ARGS += -GRX_DROP_WHEN_FULL=$(PARAM_RX_DROP_WHEN_FULL)
|
||||
|
||||
ifeq ($(WAVES), 1)
|
||||
COMPILE_ARGS += --trace-fst
|
||||
endif
|
||||
endif
|
||||
|
||||
iverilog_dump.v:
|
||||
echo 'module iverilog_dump();' > $@
|
||||
echo 'initial begin' >> $@
|
||||
echo ' $$dumpfile("$(TOPLEVEL).fst");' >> $@
|
||||
echo ' $$dumpvars(0, $(TOPLEVEL));' >> $@
|
||||
echo 'end' >> $@
|
||||
echo 'endmodule' >> $@
|
||||
|
||||
clean::
|
||||
@rm -rf sim_build_*
|
||||
@rm -rf iverilog_dump.v
|
||||
@rm -rf dump.fst $(TOPLEVEL).fst
|
||||
|
||||
include $(shell cocotb-config --makefiles)/Makefile.sim
|
||||
|
269
tb/eth_mac_1g_fifo/test_eth_mac_1g_fifo.py
Normal file
269
tb/eth_mac_1g_fifo/test_eth_mac_1g_fifo.py
Normal file
@ -0,0 +1,269 @@
|
||||
#!/usr/bin/env python
|
||||
"""
|
||||
|
||||
Copyright (c) 2020 Alex Forencich
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
"""
|
||||
|
||||
import itertools
|
||||
import logging
|
||||
import os
|
||||
|
||||
import cocotb_test.simulator
|
||||
|
||||
import cocotb
|
||||
from cocotb.clock import Clock
|
||||
from cocotb.triggers import RisingEdge
|
||||
from cocotb.regression import TestFactory
|
||||
|
||||
from cocotbext.eth import GmiiFrame, GmiiSource, GmiiSink
|
||||
from cocotbext.axi import AxiStreamSource, AxiStreamSink
|
||||
|
||||
|
||||
class TB:
|
||||
def __init__(self, dut):
|
||||
self.dut = dut
|
||||
|
||||
self.log = logging.getLogger("cocotb.tb")
|
||||
self.log.setLevel(logging.DEBUG)
|
||||
|
||||
self._enable_generator_rx = None
|
||||
self._enable_generator_tx = None
|
||||
self._enable_cr_rx = None
|
||||
self._enable_cr_tx = None
|
||||
|
||||
cocotb.fork(Clock(dut.logic_clk, 8, units="ns").start())
|
||||
cocotb.fork(Clock(dut.rx_clk, 8, units="ns").start())
|
||||
cocotb.fork(Clock(dut.tx_clk, 8, units="ns").start())
|
||||
|
||||
self.gmii_source = GmiiSource(dut.gmii_rxd, dut.gmii_rx_er, dut.gmii_rx_dv,
|
||||
dut.rx_clk, dut.rx_rst, dut.rx_clk_enable, dut.rx_mii_select)
|
||||
self.gmii_sink = GmiiSink(dut.gmii_txd, dut.gmii_tx_er, dut.gmii_tx_en,
|
||||
dut.tx_clk, dut.tx_rst, dut.tx_clk_enable, dut.tx_mii_select)
|
||||
|
||||
self.axis_source = AxiStreamSource(dut, "tx_axis", dut.logic_clk, dut.logic_rst)
|
||||
self.axis_sink = AxiStreamSink(dut, "rx_axis", dut.logic_clk, dut.logic_rst)
|
||||
|
||||
dut.rx_clk_enable.setimmediatevalue(1)
|
||||
dut.tx_clk_enable.setimmediatevalue(1)
|
||||
dut.rx_mii_select.setimmediatevalue(0)
|
||||
dut.tx_mii_select.setimmediatevalue(0)
|
||||
dut.ifg_delay.setimmediatevalue(0)
|
||||
|
||||
async def reset(self):
|
||||
self.dut.logic_rst.setimmediatevalue(0)
|
||||
self.dut.rx_rst.setimmediatevalue(0)
|
||||
self.dut.tx_rst.setimmediatevalue(0)
|
||||
for k in range(10):
|
||||
await RisingEdge(self.dut.logic_clk)
|
||||
self.dut.logic_rst <= 1
|
||||
self.dut.rx_rst <= 1
|
||||
self.dut.tx_rst <= 1
|
||||
for k in range(10):
|
||||
await RisingEdge(self.dut.logic_clk)
|
||||
self.dut.logic_rst <= 0
|
||||
self.dut.rx_rst <= 0
|
||||
self.dut.tx_rst <= 0
|
||||
for k in range(10):
|
||||
await RisingEdge(self.dut.logic_clk)
|
||||
|
||||
def set_enable_generator_rx(self, generator=None):
|
||||
if self._enable_cr_rx is not None:
|
||||
self._enable_cr_rx.kill()
|
||||
self._enable_cr_rx = None
|
||||
|
||||
self._enable_generator_rx = generator
|
||||
|
||||
if self._enable_generator_rx is not None:
|
||||
self._enable_cr_rx = cocotb.fork(self._run_enable_rx())
|
||||
|
||||
def set_enable_generator_tx(self, generator=None):
|
||||
if self._enable_cr_tx is not None:
|
||||
self._enable_cr_tx.kill()
|
||||
self._enable_cr_tx = None
|
||||
|
||||
self._enable_generator_tx = generator
|
||||
|
||||
if self._enable_generator_tx is not None:
|
||||
self._enable_cr_tx = cocotb.fork(self._run_enable_tx())
|
||||
|
||||
def clear_enable_generator_rx(self):
|
||||
self.set_enable_generator_rx(None)
|
||||
|
||||
def clear_enable_generator_tx(self):
|
||||
self.set_enable_generator_tx(None)
|
||||
|
||||
async def _run_enable_rx(self):
|
||||
for val in self._enable_generator_rx:
|
||||
self.dut.rx_clk_enable <= val
|
||||
await RisingEdge(self.dut.rx_clk)
|
||||
|
||||
async def _run_enable_tx(self):
|
||||
for val in self._enable_generator_tx:
|
||||
self.dut.tx_clk_enable <= val
|
||||
await RisingEdge(self.dut.tx_clk)
|
||||
|
||||
|
||||
async def run_test_rx(dut, payload_lengths=None, payload_data=None, ifg=12, enable_gen=None, mii_sel=False):
|
||||
|
||||
tb = TB(dut)
|
||||
|
||||
tb.gmii_source.ifg = ifg
|
||||
tb.dut.ifg_delay <= ifg
|
||||
tb.dut.rx_mii_select <= mii_sel
|
||||
tb.dut.tx_mii_select <= mii_sel
|
||||
|
||||
if enable_gen is not None:
|
||||
tb.set_enable_generator_rx(enable_gen())
|
||||
tb.set_enable_generator_tx(enable_gen())
|
||||
|
||||
await tb.reset()
|
||||
|
||||
test_frames = [payload_data(x) for x in payload_lengths()]
|
||||
|
||||
for test_data in test_frames:
|
||||
test_frame = GmiiFrame.from_payload(test_data)
|
||||
await tb.gmii_source.send(test_frame)
|
||||
|
||||
for test_data in test_frames:
|
||||
rx_frame = await tb.axis_sink.recv()
|
||||
|
||||
assert rx_frame.tdata == test_data
|
||||
assert rx_frame.tuser == 0
|
||||
|
||||
assert tb.axis_sink.empty()
|
||||
|
||||
await RisingEdge(dut.logic_clk)
|
||||
await RisingEdge(dut.logic_clk)
|
||||
|
||||
|
||||
async def run_test_tx(dut, payload_lengths=None, payload_data=None, ifg=12, enable_gen=None, mii_sel=False):
|
||||
|
||||
tb = TB(dut)
|
||||
|
||||
tb.gmii_source.ifg = ifg
|
||||
tb.dut.ifg_delay <= ifg
|
||||
tb.dut.rx_mii_select <= mii_sel
|
||||
tb.dut.tx_mii_select <= mii_sel
|
||||
|
||||
if enable_gen is not None:
|
||||
tb.set_enable_generator_rx(enable_gen())
|
||||
tb.set_enable_generator_tx(enable_gen())
|
||||
|
||||
await tb.reset()
|
||||
|
||||
test_frames = [payload_data(x) for x in payload_lengths()]
|
||||
|
||||
for test_data in test_frames:
|
||||
await tb.axis_source.send(test_data)
|
||||
|
||||
for test_data in test_frames:
|
||||
rx_frame = await tb.gmii_sink.recv()
|
||||
|
||||
assert rx_frame.get_payload() == test_data
|
||||
assert rx_frame.check_fcs()
|
||||
assert rx_frame.error is None
|
||||
|
||||
assert tb.gmii_sink.empty()
|
||||
|
||||
await RisingEdge(dut.logic_clk)
|
||||
await RisingEdge(dut.logic_clk)
|
||||
|
||||
|
||||
def size_list():
|
||||
return list(range(60, 128)) + [512, 1514] + [60]*10
|
||||
|
||||
|
||||
def incrementing_payload(length):
|
||||
return bytearray(itertools.islice(itertools.cycle(range(256)), length))
|
||||
|
||||
|
||||
def cycle_en():
|
||||
return itertools.cycle([0, 0, 0, 1])
|
||||
|
||||
|
||||
if cocotb.SIM_NAME:
|
||||
|
||||
for test in [run_test_rx, run_test_tx]:
|
||||
|
||||
factory = TestFactory(test)
|
||||
factory.add_option("payload_lengths", [size_list])
|
||||
factory.add_option("payload_data", [incrementing_payload])
|
||||
factory.add_option("ifg", [12])
|
||||
factory.add_option("enable_gen", [None, cycle_en])
|
||||
factory.add_option("mii_sel", [False, True])
|
||||
factory.generate_tests()
|
||||
|
||||
|
||||
# cocotb-test
|
||||
|
||||
tests_dir = os.path.abspath(os.path.dirname(__file__))
|
||||
rtl_dir = os.path.abspath(os.path.join(tests_dir, '..', '..', 'rtl'))
|
||||
lib_dir = os.path.abspath(os.path.join(rtl_dir, '..', 'lib'))
|
||||
axis_rtl_dir = os.path.abspath(os.path.join(lib_dir, 'axis', 'rtl'))
|
||||
|
||||
|
||||
def test_eth_mac_1g_fifo(request):
|
||||
dut = "eth_mac_1g_fifo"
|
||||
module = os.path.splitext(os.path.basename(__file__))[0]
|
||||
toplevel = dut
|
||||
|
||||
verilog_sources = [
|
||||
os.path.join(rtl_dir, f"{dut}.v"),
|
||||
os.path.join(rtl_dir, "eth_mac_1g.v"),
|
||||
os.path.join(rtl_dir, "axis_gmii_rx.v"),
|
||||
os.path.join(rtl_dir, "axis_gmii_tx.v"),
|
||||
os.path.join(rtl_dir, "lfsr.v"),
|
||||
os.path.join(axis_rtl_dir, "axis_adapter.v"),
|
||||
os.path.join(axis_rtl_dir, "axis_async_fifo.v"),
|
||||
os.path.join(axis_rtl_dir, "axis_async_fifo_adapter.v"),
|
||||
]
|
||||
|
||||
parameters = {}
|
||||
|
||||
parameters['AXIS_DATA_WIDTH'] = 8
|
||||
parameters['AXIS_KEEP_ENABLE'] = int(parameters['AXIS_DATA_WIDTH'] > 8)
|
||||
parameters['AXIS_KEEP_WIDTH'] = parameters['AXIS_DATA_WIDTH'] // 8
|
||||
parameters['ENABLE_PADDING'] = 1
|
||||
parameters['MIN_FRAME_LENGTH'] = 64
|
||||
parameters['TX_FIFO_DEPTH'] = 16384
|
||||
parameters['TX_FRAME_FIFO'] = 1
|
||||
parameters['TX_DROP_BAD_FRAME'] = parameters['TX_FRAME_FIFO']
|
||||
parameters['TX_DROP_WHEN_FULL'] = 0
|
||||
parameters['RX_FIFO_DEPTH'] = 16384
|
||||
parameters['RX_FRAME_FIFO'] = 1
|
||||
parameters['RX_DROP_BAD_FRAME'] = parameters['RX_FRAME_FIFO']
|
||||
parameters['RX_DROP_WHEN_FULL'] = parameters['RX_FRAME_FIFO']
|
||||
|
||||
extra_env = {f'PARAM_{k}': str(v) for k, v in parameters.items()}
|
||||
|
||||
sim_build = os.path.join(tests_dir,
|
||||
"sim_build_"+request.node.name.replace('[', '-').replace(']', ''))
|
||||
|
||||
cocotb_test.simulator.run(
|
||||
python_search=[tests_dir],
|
||||
verilog_sources=verilog_sources,
|
||||
toplevel=toplevel,
|
||||
module=module,
|
||||
parameters=parameters,
|
||||
sim_build=sim_build,
|
||||
extra_env=extra_env,
|
||||
)
|
83
tb/eth_mac_1g_gmii/Makefile
Normal file
83
tb/eth_mac_1g_gmii/Makefile
Normal file
@ -0,0 +1,83 @@
|
||||
# Copyright (c) 2020 Alex Forencich
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
|
||||
TOPLEVEL_LANG = verilog
|
||||
|
||||
SIM ?= icarus
|
||||
WAVES ?= 0
|
||||
|
||||
COCOTB_HDL_TIMEUNIT = 1ns
|
||||
COCOTB_HDL_TIMEPRECISION = 1ps
|
||||
|
||||
DUT = eth_mac_1g_gmii
|
||||
TOPLEVEL = $(DUT)
|
||||
MODULE = test_$(DUT)
|
||||
VERILOG_SOURCES += ../../rtl/$(DUT).v
|
||||
VERILOG_SOURCES += ../../rtl/gmii_phy_if.v
|
||||
VERILOG_SOURCES += ../../rtl/ssio_sdr_in.v
|
||||
VERILOG_SOURCES += ../../rtl/ssio_sdr_out.v
|
||||
VERILOG_SOURCES += ../../rtl/oddr.v
|
||||
VERILOG_SOURCES += ../../rtl/eth_mac_1g.v
|
||||
VERILOG_SOURCES += ../../rtl/axis_gmii_rx.v
|
||||
VERILOG_SOURCES += ../../rtl/axis_gmii_tx.v
|
||||
VERILOG_SOURCES += ../../rtl/lfsr.v
|
||||
|
||||
# module parameters
|
||||
export PARAM_ENABLE_PADDING ?= 1
|
||||
export PARAM_MIN_FRAME_LENGTH ?= 64
|
||||
|
||||
SIM_BUILD ?= sim_build_$(MODULE)
|
||||
|
||||
ifeq ($(SIM), icarus)
|
||||
PLUSARGS += -fst
|
||||
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).ENABLE_PADDING=$(PARAM_ENABLE_PADDING)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).MIN_FRAME_LENGTH=$(PARAM_MIN_FRAME_LENGTH)
|
||||
|
||||
ifeq ($(WAVES), 1)
|
||||
VERILOG_SOURCES += iverilog_dump.v
|
||||
COMPILE_ARGS += -s iverilog_dump
|
||||
endif
|
||||
else ifeq ($(SIM), verilator)
|
||||
COMPILE_ARGS += -Wno-SELRANGE -Wno-WIDTH
|
||||
|
||||
COMPILE_ARGS += -GENABLE_PADDING=$(PARAM_ENABLE_PADDING)
|
||||
COMPILE_ARGS += -GMIN_FRAME_LENGTH=$(PARAM_MIN_FRAME_LENGTH)
|
||||
|
||||
ifeq ($(WAVES), 1)
|
||||
COMPILE_ARGS += --trace-fst
|
||||
endif
|
||||
endif
|
||||
|
||||
iverilog_dump.v:
|
||||
echo 'module iverilog_dump();' > $@
|
||||
echo 'initial begin' >> $@
|
||||
echo ' $$dumpfile("$(TOPLEVEL).fst");' >> $@
|
||||
echo ' $$dumpvars(0, $(TOPLEVEL));' >> $@
|
||||
echo 'end' >> $@
|
||||
echo 'endmodule' >> $@
|
||||
|
||||
clean::
|
||||
@rm -rf sim_build_*
|
||||
@rm -rf iverilog_dump.v
|
||||
@rm -rf dump.fst $(TOPLEVEL).fst
|
||||
|
||||
include $(shell cocotb-config --makefiles)/Makefile.sim
|
||||
|
218
tb/eth_mac_1g_gmii/test_eth_mac_1g_gmii.py
Normal file
218
tb/eth_mac_1g_gmii/test_eth_mac_1g_gmii.py
Normal file
@ -0,0 +1,218 @@
|
||||
#!/usr/bin/env python
|
||||
"""
|
||||
|
||||
Copyright (c) 2020 Alex Forencich
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
"""
|
||||
|
||||
import itertools
|
||||
import logging
|
||||
import os
|
||||
|
||||
import cocotb_test.simulator
|
||||
|
||||
import cocotb
|
||||
from cocotb.clock import Clock
|
||||
from cocotb.triggers import RisingEdge
|
||||
from cocotb.regression import TestFactory
|
||||
|
||||
from cocotbext.eth import GmiiFrame, GmiiPhy
|
||||
from cocotbext.axi import AxiStreamSource, AxiStreamSink
|
||||
|
||||
|
||||
class TB:
|
||||
def __init__(self, dut, speed=1000e6):
|
||||
self.dut = dut
|
||||
|
||||
self.log = logging.getLogger("cocotb.tb")
|
||||
self.log.setLevel(logging.DEBUG)
|
||||
|
||||
cocotb.fork(Clock(dut.gtx_clk, 8, units="ns").start())
|
||||
|
||||
self.gmii_phy = GmiiPhy(dut.gmii_txd, dut.gmii_tx_er, dut.gmii_tx_en, dut.mii_tx_clk, dut.gmii_tx_clk,
|
||||
dut.gmii_rxd, dut.gmii_rx_er, dut.gmii_rx_dv, dut.gmii_rx_clk, speed=speed)
|
||||
|
||||
self.axis_source = AxiStreamSource(dut, "tx_axis", dut.tx_clk, dut.tx_rst)
|
||||
self.axis_sink = AxiStreamSink(dut, "rx_axis", dut.rx_clk, dut.rx_rst)
|
||||
|
||||
dut.ifg_delay.setimmediatevalue(0)
|
||||
|
||||
async def reset(self):
|
||||
self.dut.gtx_rst.setimmediatevalue(0)
|
||||
await RisingEdge(self.dut.tx_clk)
|
||||
await RisingEdge(self.dut.tx_clk)
|
||||
self.dut.gtx_rst <= 1
|
||||
await RisingEdge(self.dut.tx_clk)
|
||||
await RisingEdge(self.dut.tx_clk)
|
||||
self.dut.gtx_rst <= 0
|
||||
await RisingEdge(self.dut.tx_clk)
|
||||
await RisingEdge(self.dut.tx_clk)
|
||||
|
||||
def set_speed(self, speed):
|
||||
pass
|
||||
|
||||
|
||||
async def run_test_rx(dut, payload_lengths=None, payload_data=None, ifg=12, speed=1000e6):
|
||||
|
||||
tb = TB(dut, speed)
|
||||
|
||||
tb.gmii_phy.rx.ifg = ifg
|
||||
tb.dut.ifg_delay <= ifg
|
||||
|
||||
tb.set_speed(speed)
|
||||
|
||||
await tb.reset()
|
||||
|
||||
for k in range(100):
|
||||
await RisingEdge(dut.rx_clk)
|
||||
|
||||
if speed == 10e6:
|
||||
assert dut.speed == 0
|
||||
elif speed == 100e6:
|
||||
assert dut.speed == 1
|
||||
else:
|
||||
assert dut.speed == 2
|
||||
|
||||
test_frames = [payload_data(x) for x in payload_lengths()]
|
||||
|
||||
for test_data in test_frames:
|
||||
test_frame = GmiiFrame.from_payload(test_data)
|
||||
await tb.gmii_phy.rx.send(test_frame)
|
||||
|
||||
for test_data in test_frames:
|
||||
rx_frame = await tb.axis_sink.recv()
|
||||
|
||||
assert rx_frame.tdata == test_data
|
||||
assert rx_frame.tuser == 0
|
||||
|
||||
assert tb.axis_sink.empty()
|
||||
|
||||
await RisingEdge(dut.rx_clk)
|
||||
await RisingEdge(dut.rx_clk)
|
||||
|
||||
|
||||
async def run_test_tx(dut, payload_lengths=None, payload_data=None, ifg=12, speed=1000e6):
|
||||
|
||||
tb = TB(dut, speed)
|
||||
|
||||
tb.gmii_phy.rx.ifg = ifg
|
||||
tb.dut.ifg_delay <= ifg
|
||||
|
||||
tb.set_speed(speed)
|
||||
|
||||
await tb.reset()
|
||||
|
||||
for k in range(100):
|
||||
await RisingEdge(dut.rx_clk)
|
||||
|
||||
if speed == 10e6:
|
||||
assert dut.speed == 0
|
||||
elif speed == 100e6:
|
||||
assert dut.speed == 1
|
||||
else:
|
||||
assert dut.speed == 2
|
||||
|
||||
test_frames = [payload_data(x) for x in payload_lengths()]
|
||||
|
||||
for test_data in test_frames:
|
||||
await tb.axis_source.send(test_data)
|
||||
|
||||
for test_data in test_frames:
|
||||
rx_frame = await tb.gmii_phy.tx.recv()
|
||||
|
||||
assert rx_frame.get_payload() == test_data
|
||||
assert rx_frame.check_fcs()
|
||||
assert rx_frame.error is None
|
||||
|
||||
assert tb.gmii_phy.tx.empty()
|
||||
|
||||
await RisingEdge(dut.tx_clk)
|
||||
await RisingEdge(dut.tx_clk)
|
||||
|
||||
|
||||
def size_list():
|
||||
return list(range(60, 128)) + [512, 1514] + [60]*10
|
||||
|
||||
|
||||
def incrementing_payload(length):
|
||||
return bytearray(itertools.islice(itertools.cycle(range(256)), length))
|
||||
|
||||
|
||||
def cycle_en():
|
||||
return itertools.cycle([0, 0, 0, 1])
|
||||
|
||||
|
||||
if cocotb.SIM_NAME:
|
||||
|
||||
for test in [run_test_rx, run_test_tx]:
|
||||
|
||||
factory = TestFactory(test)
|
||||
factory.add_option("payload_lengths", [size_list])
|
||||
factory.add_option("payload_data", [incrementing_payload])
|
||||
factory.add_option("ifg", [12])
|
||||
factory.add_option("speed", [1000e6, 100e6, 10e6])
|
||||
factory.generate_tests()
|
||||
|
||||
|
||||
# cocotb-test
|
||||
|
||||
tests_dir = os.path.abspath(os.path.dirname(__file__))
|
||||
rtl_dir = os.path.abspath(os.path.join(tests_dir, '..', '..', 'rtl'))
|
||||
lib_dir = os.path.abspath(os.path.join(rtl_dir, '..', 'lib'))
|
||||
axis_rtl_dir = os.path.abspath(os.path.join(lib_dir, 'axis', 'rtl'))
|
||||
|
||||
|
||||
def test_eth_mac_1g_gmii(request):
|
||||
dut = "eth_mac_1g_gmii"
|
||||
module = os.path.splitext(os.path.basename(__file__))[0]
|
||||
toplevel = dut
|
||||
|
||||
verilog_sources = [
|
||||
os.path.join(rtl_dir, f"{dut}.v"),
|
||||
os.path.join(rtl_dir, "gmii_phy_if.v"),
|
||||
os.path.join(rtl_dir, "ssio_sdr_in.v"),
|
||||
os.path.join(rtl_dir, "ssio_sdr_out.v"),
|
||||
os.path.join(rtl_dir, "oddr.v"),
|
||||
os.path.join(rtl_dir, "eth_mac_1g.v"),
|
||||
os.path.join(rtl_dir, "axis_gmii_rx.v"),
|
||||
os.path.join(rtl_dir, "axis_gmii_tx.v"),
|
||||
os.path.join(rtl_dir, "lfsr.v"),
|
||||
]
|
||||
|
||||
parameters = {}
|
||||
|
||||
parameters['ENABLE_PADDING'] = 1
|
||||
parameters['MIN_FRAME_LENGTH'] = 64
|
||||
|
||||
extra_env = {f'PARAM_{k}': str(v) for k, v in parameters.items()}
|
||||
|
||||
sim_build = os.path.join(tests_dir,
|
||||
"sim_build_"+request.node.name.replace('[', '-').replace(']', ''))
|
||||
|
||||
cocotb_test.simulator.run(
|
||||
python_search=[tests_dir],
|
||||
verilog_sources=verilog_sources,
|
||||
toplevel=toplevel,
|
||||
module=module,
|
||||
parameters=parameters,
|
||||
sim_build=sim_build,
|
||||
extra_env=extra_env,
|
||||
)
|
120
tb/eth_mac_1g_gmii_fifo/Makefile
Normal file
120
tb/eth_mac_1g_gmii_fifo/Makefile
Normal file
@ -0,0 +1,120 @@
|
||||
# Copyright (c) 2020 Alex Forencich
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
|
||||
TOPLEVEL_LANG = verilog
|
||||
|
||||
SIM ?= icarus
|
||||
WAVES ?= 0
|
||||
|
||||
COCOTB_HDL_TIMEUNIT = 1ns
|
||||
COCOTB_HDL_TIMEPRECISION = 1ps
|
||||
|
||||
DUT = eth_mac_1g_gmii_fifo
|
||||
TOPLEVEL = $(DUT)
|
||||
MODULE = test_$(DUT)
|
||||
VERILOG_SOURCES += ../../rtl/$(DUT).v
|
||||
VERILOG_SOURCES += ../../rtl/eth_mac_1g_gmii.v
|
||||
VERILOG_SOURCES += ../../rtl/gmii_phy_if.v
|
||||
VERILOG_SOURCES += ../../rtl/ssio_sdr_in.v
|
||||
VERILOG_SOURCES += ../../rtl/ssio_sdr_out.v
|
||||
VERILOG_SOURCES += ../../rtl/oddr.v
|
||||
VERILOG_SOURCES += ../../rtl/eth_mac_1g.v
|
||||
VERILOG_SOURCES += ../../rtl/axis_gmii_rx.v
|
||||
VERILOG_SOURCES += ../../rtl/axis_gmii_tx.v
|
||||
VERILOG_SOURCES += ../../rtl/lfsr.v
|
||||
VERILOG_SOURCES += ../../lib/axis/rtl/axis_adapter.v
|
||||
VERILOG_SOURCES += ../../lib/axis/rtl/axis_async_fifo.v
|
||||
VERILOG_SOURCES += ../../lib/axis/rtl/axis_async_fifo_adapter.v
|
||||
|
||||
# module parameters
|
||||
export PARAM_AXIS_DATA_WIDTH ?= 8
|
||||
export PARAM_AXIS_KEEP_ENABLE ?= $(shell expr $(PARAM_AXIS_DATA_WIDTH) \> 8 )
|
||||
export PARAM_AXIS_KEEP_WIDTH ?= $(shell expr $(PARAM_AXIS_DATA_WIDTH) / 8 )
|
||||
export PARAM_ENABLE_PADDING ?= 1
|
||||
export PARAM_MIN_FRAME_LENGTH ?= 64
|
||||
export PARAM_TX_FIFO_DEPTH ?= 16384
|
||||
export PARAM_TX_FRAME_FIFO ?= 1
|
||||
export PARAM_TX_DROP_BAD_FRAME ?= $(PARAM_TX_FRAME_FIFO)
|
||||
export PARAM_TX_DROP_WHEN_FULL ?= 0
|
||||
export PARAM_RX_FIFO_DEPTH ?= 16384
|
||||
export PARAM_RX_FRAME_FIFO ?= 1
|
||||
export PARAM_RX_DROP_BAD_FRAME ?= $(PARAM_RX_FRAME_FIFO)
|
||||
export PARAM_RX_DROP_WHEN_FULL ?= $(PARAM_RX_FRAME_FIFO)
|
||||
|
||||
SIM_BUILD ?= sim_build_$(MODULE)
|
||||
|
||||
ifeq ($(SIM), icarus)
|
||||
PLUSARGS += -fst
|
||||
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).AXIS_DATA_WIDTH=$(PARAM_AXIS_DATA_WIDTH)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).AXIS_KEEP_ENABLE=$(PARAM_AXIS_KEEP_ENABLE)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).AXIS_KEEP_WIDTH=$(PARAM_AXIS_KEEP_WIDTH)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).ENABLE_PADDING=$(PARAM_ENABLE_PADDING)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).MIN_FRAME_LENGTH=$(PARAM_MIN_FRAME_LENGTH)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).TX_FIFO_DEPTH=$(PARAM_TX_FIFO_DEPTH)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).TX_FRAME_FIFO=$(PARAM_TX_FRAME_FIFO)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).TX_DROP_BAD_FRAME=$(PARAM_TX_DROP_BAD_FRAME)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).TX_DROP_WHEN_FULL=$(PARAM_TX_DROP_WHEN_FULL)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).RX_FIFO_DEPTH=$(PARAM_RX_FIFO_DEPTH)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).RX_FRAME_FIFO=$(PARAM_RX_FRAME_FIFO)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).RX_DROP_BAD_FRAME=$(PARAM_RX_DROP_BAD_FRAME)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).RX_DROP_WHEN_FULL=$(PARAM_RX_DROP_WHEN_FULL)
|
||||
|
||||
ifeq ($(WAVES), 1)
|
||||
VERILOG_SOURCES += iverilog_dump.v
|
||||
COMPILE_ARGS += -s iverilog_dump
|
||||
endif
|
||||
else ifeq ($(SIM), verilator)
|
||||
COMPILE_ARGS += -Wno-SELRANGE -Wno-WIDTH
|
||||
|
||||
COMPILE_ARGS += -GAXIS_DATA_WIDTH=$(PARAM_AXIS_DATA_WIDTH)
|
||||
COMPILE_ARGS += -GAXIS_KEEP_ENABLE=$(PARAM_AXIS_KEEP_ENABLE)
|
||||
COMPILE_ARGS += -GAXIS_KEEP_WIDTH=$(PARAM_AXIS_KEEP_WIDTH)
|
||||
COMPILE_ARGS += -GENABLE_PADDING=$(PARAM_ENABLE_PADDING)
|
||||
COMPILE_ARGS += -GMIN_FRAME_LENGTH=$(PARAM_MIN_FRAME_LENGTH)
|
||||
COMPILE_ARGS += -GTX_FIFO_DEPTH=$(PARAM_TX_FIFO_DEPTH)
|
||||
COMPILE_ARGS += -GTX_FRAME_FIFO=$(PARAM_TX_FRAME_FIFO)
|
||||
COMPILE_ARGS += -GTX_DROP_BAD_FRAME=$(PARAM_TX_DROP_BAD_FRAME)
|
||||
COMPILE_ARGS += -GTX_DROP_WHEN_FULL=$(PARAM_TX_DROP_WHEN_FULL)
|
||||
COMPILE_ARGS += -GRX_FIFO_DEPTH=$(PARAM_RX_FIFO_DEPTH)
|
||||
COMPILE_ARGS += -GRX_FRAME_FIFO=$(PARAM_RX_FRAME_FIFO)
|
||||
COMPILE_ARGS += -GRX_DROP_BAD_FRAME=$(PARAM_RX_DROP_BAD_FRAME)
|
||||
COMPILE_ARGS += -GRX_DROP_WHEN_FULL=$(PARAM_RX_DROP_WHEN_FULL)
|
||||
|
||||
ifeq ($(WAVES), 1)
|
||||
COMPILE_ARGS += --trace-fst
|
||||
endif
|
||||
endif
|
||||
|
||||
iverilog_dump.v:
|
||||
echo 'module iverilog_dump();' > $@
|
||||
echo 'initial begin' >> $@
|
||||
echo ' $$dumpfile("$(TOPLEVEL).fst");' >> $@
|
||||
echo ' $$dumpvars(0, $(TOPLEVEL));' >> $@
|
||||
echo 'end' >> $@
|
||||
echo 'endmodule' >> $@
|
||||
|
||||
clean::
|
||||
@rm -rf sim_build_*
|
||||
@rm -rf iverilog_dump.v
|
||||
@rm -rf dump.fst $(TOPLEVEL).fst
|
||||
|
||||
include $(shell cocotb-config --makefiles)/Makefile.sim
|
||||
|
237
tb/eth_mac_1g_gmii_fifo/test_eth_mac_1g_gmii_fifo.py
Normal file
237
tb/eth_mac_1g_gmii_fifo/test_eth_mac_1g_gmii_fifo.py
Normal file
@ -0,0 +1,237 @@
|
||||
#!/usr/bin/env python
|
||||
"""
|
||||
|
||||
Copyright (c) 2020 Alex Forencich
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
"""
|
||||
|
||||
import itertools
|
||||
import logging
|
||||
import os
|
||||
|
||||
import cocotb_test.simulator
|
||||
|
||||
import cocotb
|
||||
from cocotb.clock import Clock
|
||||
from cocotb.triggers import RisingEdge
|
||||
from cocotb.regression import TestFactory
|
||||
|
||||
from cocotbext.eth import GmiiFrame, GmiiPhy
|
||||
from cocotbext.axi import AxiStreamSource, AxiStreamSink
|
||||
|
||||
|
||||
class TB:
|
||||
def __init__(self, dut, speed=1000e6):
|
||||
self.dut = dut
|
||||
|
||||
self.log = logging.getLogger("cocotb.tb")
|
||||
self.log.setLevel(logging.DEBUG)
|
||||
|
||||
cocotb.fork(Clock(dut.gtx_clk, 8, units="ns").start())
|
||||
cocotb.fork(Clock(dut.logic_clk, 8, units="ns").start())
|
||||
|
||||
self.gmii_phy = GmiiPhy(dut.gmii_txd, dut.gmii_tx_er, dut.gmii_tx_en, dut.mii_tx_clk, dut.gmii_tx_clk,
|
||||
dut.gmii_rxd, dut.gmii_rx_er, dut.gmii_rx_dv, dut.gmii_rx_clk, speed=speed)
|
||||
|
||||
self.axis_source = AxiStreamSource(dut, "tx_axis", dut.logic_clk, dut.logic_rst)
|
||||
self.axis_sink = AxiStreamSink(dut, "rx_axis", dut.logic_clk, dut.logic_rst)
|
||||
|
||||
dut.ifg_delay.setimmediatevalue(0)
|
||||
|
||||
async def reset(self):
|
||||
self.dut.gtx_rst.setimmediatevalue(0)
|
||||
self.dut.logic_rst.setimmediatevalue(0)
|
||||
await RisingEdge(self.dut.tx_clk)
|
||||
await RisingEdge(self.dut.tx_clk)
|
||||
self.dut.gtx_rst <= 1
|
||||
self.dut.logic_rst <= 1
|
||||
await RisingEdge(self.dut.tx_clk)
|
||||
await RisingEdge(self.dut.tx_clk)
|
||||
self.dut.gtx_rst <= 0
|
||||
self.dut.logic_rst <= 0
|
||||
await RisingEdge(self.dut.tx_clk)
|
||||
await RisingEdge(self.dut.tx_clk)
|
||||
|
||||
def set_speed(self, speed):
|
||||
pass
|
||||
|
||||
|
||||
async def run_test_rx(dut, payload_lengths=None, payload_data=None, ifg=12, speed=1000e6):
|
||||
|
||||
tb = TB(dut, speed)
|
||||
|
||||
tb.gmii_phy.rx.ifg = ifg
|
||||
tb.dut.ifg_delay <= ifg
|
||||
|
||||
tb.set_speed(speed)
|
||||
|
||||
await tb.reset()
|
||||
|
||||
for k in range(100):
|
||||
await RisingEdge(dut.rx_clk)
|
||||
|
||||
if speed == 10e6:
|
||||
assert dut.speed == 0
|
||||
elif speed == 100e6:
|
||||
assert dut.speed == 1
|
||||
else:
|
||||
assert dut.speed == 2
|
||||
|
||||
test_frames = [payload_data(x) for x in payload_lengths()]
|
||||
|
||||
for test_data in test_frames:
|
||||
test_frame = GmiiFrame.from_payload(test_data)
|
||||
await tb.gmii_phy.rx.send(test_frame)
|
||||
|
||||
for test_data in test_frames:
|
||||
rx_frame = await tb.axis_sink.recv()
|
||||
|
||||
assert rx_frame.tdata == test_data
|
||||
assert rx_frame.tuser == 0
|
||||
|
||||
assert tb.axis_sink.empty()
|
||||
|
||||
await RisingEdge(dut.rx_clk)
|
||||
await RisingEdge(dut.rx_clk)
|
||||
|
||||
|
||||
async def run_test_tx(dut, payload_lengths=None, payload_data=None, ifg=12, speed=1000e6):
|
||||
|
||||
tb = TB(dut, speed)
|
||||
|
||||
tb.gmii_phy.rx.ifg = ifg
|
||||
tb.dut.ifg_delay <= ifg
|
||||
|
||||
tb.set_speed(speed)
|
||||
|
||||
await tb.reset()
|
||||
|
||||
for k in range(100):
|
||||
await RisingEdge(dut.rx_clk)
|
||||
|
||||
if speed == 10e6:
|
||||
assert dut.speed == 0
|
||||
elif speed == 100e6:
|
||||
assert dut.speed == 1
|
||||
else:
|
||||
assert dut.speed == 2
|
||||
|
||||
test_frames = [payload_data(x) for x in payload_lengths()]
|
||||
|
||||
for test_data in test_frames:
|
||||
await tb.axis_source.send(test_data)
|
||||
|
||||
for test_data in test_frames:
|
||||
rx_frame = await tb.gmii_phy.tx.recv()
|
||||
|
||||
assert rx_frame.get_payload() == test_data
|
||||
assert rx_frame.check_fcs()
|
||||
assert rx_frame.error is None
|
||||
|
||||
assert tb.gmii_phy.tx.empty()
|
||||
|
||||
await RisingEdge(dut.tx_clk)
|
||||
await RisingEdge(dut.tx_clk)
|
||||
|
||||
|
||||
def size_list():
|
||||
return list(range(60, 128)) + [512, 1514] + [60]*10
|
||||
|
||||
|
||||
def incrementing_payload(length):
|
||||
return bytearray(itertools.islice(itertools.cycle(range(256)), length))
|
||||
|
||||
|
||||
def cycle_en():
|
||||
return itertools.cycle([0, 0, 0, 1])
|
||||
|
||||
|
||||
if cocotb.SIM_NAME:
|
||||
|
||||
for test in [run_test_rx, run_test_tx]:
|
||||
|
||||
factory = TestFactory(test)
|
||||
factory.add_option("payload_lengths", [size_list])
|
||||
factory.add_option("payload_data", [incrementing_payload])
|
||||
factory.add_option("ifg", [12])
|
||||
factory.add_option("speed", [1000e6, 100e6, 10e6])
|
||||
factory.generate_tests()
|
||||
|
||||
|
||||
# cocotb-test
|
||||
|
||||
tests_dir = os.path.abspath(os.path.dirname(__file__))
|
||||
rtl_dir = os.path.abspath(os.path.join(tests_dir, '..', '..', 'rtl'))
|
||||
lib_dir = os.path.abspath(os.path.join(rtl_dir, '..', 'lib'))
|
||||
axis_rtl_dir = os.path.abspath(os.path.join(lib_dir, 'axis', 'rtl'))
|
||||
|
||||
|
||||
def test_eth_mac_1g_gmii_fifo(request):
|
||||
dut = "eth_mac_1g_gmii_fifo"
|
||||
module = os.path.splitext(os.path.basename(__file__))[0]
|
||||
toplevel = dut
|
||||
|
||||
verilog_sources = [
|
||||
os.path.join(rtl_dir, f"{dut}.v"),
|
||||
os.path.join(rtl_dir, "eth_mac_1g_gmii.v"),
|
||||
os.path.join(rtl_dir, "gmii_phy_if.v"),
|
||||
os.path.join(rtl_dir, "ssio_sdr_in.v"),
|
||||
os.path.join(rtl_dir, "ssio_sdr_out.v"),
|
||||
os.path.join(rtl_dir, "oddr.v"),
|
||||
os.path.join(rtl_dir, "eth_mac_1g.v"),
|
||||
os.path.join(rtl_dir, "axis_gmii_rx.v"),
|
||||
os.path.join(rtl_dir, "axis_gmii_tx.v"),
|
||||
os.path.join(rtl_dir, "lfsr.v"),
|
||||
os.path.join(axis_rtl_dir, "axis_adapter.v"),
|
||||
os.path.join(axis_rtl_dir, "axis_async_fifo.v"),
|
||||
os.path.join(axis_rtl_dir, "axis_async_fifo_adapter.v"),
|
||||
]
|
||||
|
||||
parameters = {}
|
||||
|
||||
parameters['AXIS_DATA_WIDTH'] = 8
|
||||
parameters['AXIS_KEEP_ENABLE'] = int(parameters['AXIS_DATA_WIDTH'] > 8)
|
||||
parameters['AXIS_KEEP_WIDTH'] = parameters['AXIS_DATA_WIDTH'] // 8
|
||||
parameters['ENABLE_PADDING'] = 1
|
||||
parameters['MIN_FRAME_LENGTH'] = 64
|
||||
parameters['TX_FIFO_DEPTH'] = 16384
|
||||
parameters['TX_FRAME_FIFO'] = 1
|
||||
parameters['TX_DROP_BAD_FRAME'] = parameters['TX_FRAME_FIFO']
|
||||
parameters['TX_DROP_WHEN_FULL'] = 0
|
||||
parameters['RX_FIFO_DEPTH'] = 16384
|
||||
parameters['RX_FRAME_FIFO'] = 1
|
||||
parameters['RX_DROP_BAD_FRAME'] = parameters['RX_FRAME_FIFO']
|
||||
parameters['RX_DROP_WHEN_FULL'] = parameters['RX_FRAME_FIFO']
|
||||
|
||||
extra_env = {f'PARAM_{k}': str(v) for k, v in parameters.items()}
|
||||
|
||||
sim_build = os.path.join(tests_dir,
|
||||
"sim_build_"+request.node.name.replace('[', '-').replace(']', ''))
|
||||
|
||||
cocotb_test.simulator.run(
|
||||
python_search=[tests_dir],
|
||||
verilog_sources=verilog_sources,
|
||||
toplevel=toplevel,
|
||||
module=module,
|
||||
parameters=parameters,
|
||||
sim_build=sim_build,
|
||||
extra_env=extra_env,
|
||||
)
|
83
tb/eth_mac_1g_rgmii/Makefile
Normal file
83
tb/eth_mac_1g_rgmii/Makefile
Normal file
@ -0,0 +1,83 @@
|
||||
# Copyright (c) 2020 Alex Forencich
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
|
||||
TOPLEVEL_LANG = verilog
|
||||
|
||||
SIM ?= icarus
|
||||
WAVES ?= 0
|
||||
|
||||
COCOTB_HDL_TIMEUNIT = 1ns
|
||||
COCOTB_HDL_TIMEPRECISION = 1ps
|
||||
|
||||
DUT = eth_mac_1g_rgmii
|
||||
TOPLEVEL = $(DUT)
|
||||
MODULE = test_$(DUT)
|
||||
VERILOG_SOURCES += ../../rtl/$(DUT).v
|
||||
VERILOG_SOURCES += ../../rtl/iddr.v
|
||||
VERILOG_SOURCES += ../../rtl/oddr.v
|
||||
VERILOG_SOURCES += ../../rtl/ssio_ddr_in.v
|
||||
VERILOG_SOURCES += ../../rtl/rgmii_phy_if.v
|
||||
VERILOG_SOURCES += ../../rtl/eth_mac_1g.v
|
||||
VERILOG_SOURCES += ../../rtl/axis_gmii_rx.v
|
||||
VERILOG_SOURCES += ../../rtl/axis_gmii_tx.v
|
||||
VERILOG_SOURCES += ../../rtl/lfsr.v
|
||||
|
||||
# module parameters
|
||||
export PARAM_ENABLE_PADDING ?= 1
|
||||
export PARAM_MIN_FRAME_LENGTH ?= 64
|
||||
|
||||
SIM_BUILD ?= sim_build_$(MODULE)
|
||||
|
||||
ifeq ($(SIM), icarus)
|
||||
PLUSARGS += -fst
|
||||
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).ENABLE_PADDING=$(PARAM_ENABLE_PADDING)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).MIN_FRAME_LENGTH=$(PARAM_MIN_FRAME_LENGTH)
|
||||
|
||||
ifeq ($(WAVES), 1)
|
||||
VERILOG_SOURCES += iverilog_dump.v
|
||||
COMPILE_ARGS += -s iverilog_dump
|
||||
endif
|
||||
else ifeq ($(SIM), verilator)
|
||||
COMPILE_ARGS += -Wno-SELRANGE -Wno-WIDTH
|
||||
|
||||
COMPILE_ARGS += -GENABLE_PADDING=$(PARAM_ENABLE_PADDING)
|
||||
COMPILE_ARGS += -GMIN_FRAME_LENGTH=$(PARAM_MIN_FRAME_LENGTH)
|
||||
|
||||
ifeq ($(WAVES), 1)
|
||||
COMPILE_ARGS += --trace-fst
|
||||
endif
|
||||
endif
|
||||
|
||||
iverilog_dump.v:
|
||||
echo 'module iverilog_dump();' > $@
|
||||
echo 'initial begin' >> $@
|
||||
echo ' $$dumpfile("$(TOPLEVEL).fst");' >> $@
|
||||
echo ' $$dumpvars(0, $(TOPLEVEL));' >> $@
|
||||
echo 'end' >> $@
|
||||
echo 'endmodule' >> $@
|
||||
|
||||
clean::
|
||||
@rm -rf sim_build_*
|
||||
@rm -rf iverilog_dump.v
|
||||
@rm -rf dump.fst $(TOPLEVEL).fst
|
||||
|
||||
include $(shell cocotb-config --makefiles)/Makefile.sim
|
||||
|
225
tb/eth_mac_1g_rgmii/test_eth_mac_1g_rgmii.py
Normal file
225
tb/eth_mac_1g_rgmii/test_eth_mac_1g_rgmii.py
Normal file
@ -0,0 +1,225 @@
|
||||
#!/usr/bin/env python
|
||||
"""
|
||||
|
||||
Copyright (c) 2020 Alex Forencich
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
"""
|
||||
|
||||
import itertools
|
||||
import logging
|
||||
import os
|
||||
|
||||
import cocotb_test.simulator
|
||||
|
||||
import cocotb
|
||||
from cocotb.triggers import RisingEdge, Timer
|
||||
from cocotb.regression import TestFactory
|
||||
|
||||
from cocotbext.eth import GmiiFrame, RgmiiPhy
|
||||
from cocotbext.axi import AxiStreamSource, AxiStreamSink
|
||||
|
||||
|
||||
class TB:
|
||||
def __init__(self, dut, speed=1000e6):
|
||||
self.dut = dut
|
||||
|
||||
self.log = logging.getLogger("cocotb.tb")
|
||||
self.log.setLevel(logging.DEBUG)
|
||||
|
||||
self.rgmii_phy = RgmiiPhy(dut.rgmii_txd, dut.rgmii_tx_ctl, dut.rgmii_tx_clk,
|
||||
dut.rgmii_rxd, dut.rgmii_rx_ctl, dut.rgmii_rx_clk, speed=speed)
|
||||
|
||||
self.axis_source = AxiStreamSource(dut, "tx_axis", dut.tx_clk, dut.tx_rst)
|
||||
self.axis_sink = AxiStreamSink(dut, "rx_axis", dut.rx_clk, dut.rx_rst)
|
||||
|
||||
dut.ifg_delay.setimmediatevalue(0)
|
||||
|
||||
dut.gtx_clk.setimmediatevalue(0)
|
||||
dut.gtx_clk90.setimmediatevalue(0)
|
||||
|
||||
cocotb.fork(self._run_gtx_clk())
|
||||
|
||||
async def reset(self):
|
||||
self.dut.gtx_rst.setimmediatevalue(0)
|
||||
await RisingEdge(self.dut.gtx_clk)
|
||||
await RisingEdge(self.dut.gtx_clk)
|
||||
self.dut.gtx_rst <= 1
|
||||
await RisingEdge(self.dut.gtx_clk)
|
||||
await RisingEdge(self.dut.gtx_clk)
|
||||
self.dut.gtx_rst <= 0
|
||||
await RisingEdge(self.dut.gtx_clk)
|
||||
await RisingEdge(self.dut.gtx_clk)
|
||||
|
||||
async def _run_gtx_clk(self):
|
||||
t = Timer(2, 'ns')
|
||||
while True:
|
||||
self.dut.gtx_clk <= 1
|
||||
await t
|
||||
self.dut.gtx_clk90 <= 1
|
||||
await t
|
||||
self.dut.gtx_clk <= 0
|
||||
await t
|
||||
self.dut.gtx_clk90 <= 0
|
||||
await t
|
||||
|
||||
|
||||
async def run_test_rx(dut, payload_lengths=None, payload_data=None, ifg=12, speed=1000e6):
|
||||
|
||||
tb = TB(dut, speed)
|
||||
|
||||
tb.rgmii_phy.rx.ifg = ifg
|
||||
tb.dut.ifg_delay <= ifg
|
||||
|
||||
await tb.reset()
|
||||
|
||||
for k in range(100):
|
||||
await RisingEdge(dut.rx_clk)
|
||||
|
||||
if speed == 10e6:
|
||||
assert dut.speed == 0
|
||||
elif speed == 100e6:
|
||||
assert dut.speed == 1
|
||||
else:
|
||||
assert dut.speed == 2
|
||||
|
||||
test_frames = [payload_data(x) for x in payload_lengths()]
|
||||
|
||||
for test_data in test_frames:
|
||||
test_frame = GmiiFrame.from_payload(test_data)
|
||||
await tb.rgmii_phy.rx.send(test_frame)
|
||||
|
||||
for test_data in test_frames:
|
||||
rx_frame = await tb.axis_sink.recv()
|
||||
|
||||
assert rx_frame.tdata == test_data
|
||||
assert rx_frame.tuser == 0
|
||||
|
||||
assert tb.axis_sink.empty()
|
||||
|
||||
await RisingEdge(dut.rx_clk)
|
||||
await RisingEdge(dut.rx_clk)
|
||||
|
||||
|
||||
async def run_test_tx(dut, payload_lengths=None, payload_data=None, ifg=12, speed=1000e6):
|
||||
|
||||
tb = TB(dut, speed)
|
||||
|
||||
tb.rgmii_phy.rx.ifg = ifg
|
||||
tb.dut.ifg_delay <= ifg
|
||||
|
||||
await tb.reset()
|
||||
|
||||
for k in range(100):
|
||||
await RisingEdge(dut.rx_clk)
|
||||
|
||||
if speed == 10e6:
|
||||
assert dut.speed == 0
|
||||
elif speed == 100e6:
|
||||
assert dut.speed == 1
|
||||
else:
|
||||
assert dut.speed == 2
|
||||
|
||||
test_frames = [payload_data(x) for x in payload_lengths()]
|
||||
|
||||
for test_data in test_frames:
|
||||
await tb.axis_source.send(test_data)
|
||||
|
||||
for test_data in test_frames:
|
||||
rx_frame = await tb.rgmii_phy.tx.recv()
|
||||
|
||||
assert rx_frame.get_payload() == test_data
|
||||
assert rx_frame.check_fcs()
|
||||
assert rx_frame.error is None
|
||||
|
||||
assert tb.rgmii_phy.tx.empty()
|
||||
|
||||
await RisingEdge(dut.tx_clk)
|
||||
await RisingEdge(dut.tx_clk)
|
||||
|
||||
|
||||
def size_list():
|
||||
return list(range(60, 128)) + [512, 1514] + [60]*10
|
||||
|
||||
|
||||
def incrementing_payload(length):
|
||||
return bytearray(itertools.islice(itertools.cycle(range(256)), length))
|
||||
|
||||
|
||||
def cycle_en():
|
||||
return itertools.cycle([0, 0, 0, 1])
|
||||
|
||||
|
||||
if cocotb.SIM_NAME:
|
||||
|
||||
for test in [run_test_rx, run_test_tx]:
|
||||
|
||||
factory = TestFactory(test)
|
||||
factory.add_option("payload_lengths", [size_list])
|
||||
factory.add_option("payload_data", [incrementing_payload])
|
||||
factory.add_option("ifg", [12])
|
||||
factory.add_option("speed", [1000e6, 100e6, 10e6])
|
||||
factory.generate_tests()
|
||||
|
||||
|
||||
# cocotb-test
|
||||
|
||||
tests_dir = os.path.abspath(os.path.dirname(__file__))
|
||||
rtl_dir = os.path.abspath(os.path.join(tests_dir, '..', '..', 'rtl'))
|
||||
lib_dir = os.path.abspath(os.path.join(rtl_dir, '..', 'lib'))
|
||||
axis_rtl_dir = os.path.abspath(os.path.join(lib_dir, 'axis', 'rtl'))
|
||||
|
||||
|
||||
def test_eth_mac_1g_rgmii(request):
|
||||
dut = "eth_mac_1g_rgmii"
|
||||
module = os.path.splitext(os.path.basename(__file__))[0]
|
||||
toplevel = dut
|
||||
|
||||
verilog_sources = [
|
||||
os.path.join(rtl_dir, f"{dut}.v"),
|
||||
os.path.join(rtl_dir, "iddr.v"),
|
||||
os.path.join(rtl_dir, "oddr.v"),
|
||||
os.path.join(rtl_dir, "ssio_ddr_in.v"),
|
||||
os.path.join(rtl_dir, "rgmii_phy_if.v"),
|
||||
os.path.join(rtl_dir, "eth_mac_1g.v"),
|
||||
os.path.join(rtl_dir, "axis_gmii_rx.v"),
|
||||
os.path.join(rtl_dir, "axis_gmii_tx.v"),
|
||||
os.path.join(rtl_dir, "lfsr.v"),
|
||||
]
|
||||
|
||||
parameters = {}
|
||||
|
||||
parameters['ENABLE_PADDING'] = 1
|
||||
parameters['MIN_FRAME_LENGTH'] = 64
|
||||
|
||||
extra_env = {f'PARAM_{k}': str(v) for k, v in parameters.items()}
|
||||
|
||||
sim_build = os.path.join(tests_dir,
|
||||
"sim_build_"+request.node.name.replace('[', '-').replace(']', ''))
|
||||
|
||||
cocotb_test.simulator.run(
|
||||
python_search=[tests_dir],
|
||||
verilog_sources=verilog_sources,
|
||||
toplevel=toplevel,
|
||||
module=module,
|
||||
parameters=parameters,
|
||||
sim_build=sim_build,
|
||||
extra_env=extra_env,
|
||||
)
|
120
tb/eth_mac_1g_rgmii_fifo/Makefile
Normal file
120
tb/eth_mac_1g_rgmii_fifo/Makefile
Normal file
@ -0,0 +1,120 @@
|
||||
# Copyright (c) 2020 Alex Forencich
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
|
||||
TOPLEVEL_LANG = verilog
|
||||
|
||||
SIM ?= icarus
|
||||
WAVES ?= 0
|
||||
|
||||
COCOTB_HDL_TIMEUNIT = 1ns
|
||||
COCOTB_HDL_TIMEPRECISION = 1ps
|
||||
|
||||
DUT = eth_mac_1g_rgmii_fifo
|
||||
TOPLEVEL = $(DUT)
|
||||
MODULE = test_$(DUT)
|
||||
VERILOG_SOURCES += ../../rtl/$(DUT).v
|
||||
VERILOG_SOURCES += ../../rtl/eth_mac_1g_rgmii.v
|
||||
VERILOG_SOURCES += ../../rtl/iddr.v
|
||||
VERILOG_SOURCES += ../../rtl/oddr.v
|
||||
VERILOG_SOURCES += ../../rtl/ssio_ddr_in.v
|
||||
VERILOG_SOURCES += ../../rtl/rgmii_phy_if.v
|
||||
VERILOG_SOURCES += ../../rtl/eth_mac_1g.v
|
||||
VERILOG_SOURCES += ../../rtl/axis_gmii_rx.v
|
||||
VERILOG_SOURCES += ../../rtl/axis_gmii_tx.v
|
||||
VERILOG_SOURCES += ../../rtl/lfsr.v
|
||||
VERILOG_SOURCES += ../../lib/axis/rtl/axis_adapter.v
|
||||
VERILOG_SOURCES += ../../lib/axis/rtl/axis_async_fifo.v
|
||||
VERILOG_SOURCES += ../../lib/axis/rtl/axis_async_fifo_adapter.v
|
||||
|
||||
# module parameters
|
||||
export PARAM_AXIS_DATA_WIDTH ?= 8
|
||||
export PARAM_AXIS_KEEP_ENABLE ?= $(shell expr $(PARAM_AXIS_DATA_WIDTH) \> 8 )
|
||||
export PARAM_AXIS_KEEP_WIDTH ?= $(shell expr $(PARAM_AXIS_DATA_WIDTH) / 8 )
|
||||
export PARAM_ENABLE_PADDING ?= 1
|
||||
export PARAM_MIN_FRAME_LENGTH ?= 64
|
||||
export PARAM_TX_FIFO_DEPTH ?= 16384
|
||||
export PARAM_TX_FRAME_FIFO ?= 1
|
||||
export PARAM_TX_DROP_BAD_FRAME ?= $(PARAM_TX_FRAME_FIFO)
|
||||
export PARAM_TX_DROP_WHEN_FULL ?= 0
|
||||
export PARAM_RX_FIFO_DEPTH ?= 16384
|
||||
export PARAM_RX_FRAME_FIFO ?= 1
|
||||
export PARAM_RX_DROP_BAD_FRAME ?= $(PARAM_RX_FRAME_FIFO)
|
||||
export PARAM_RX_DROP_WHEN_FULL ?= $(PARAM_RX_FRAME_FIFO)
|
||||
|
||||
SIM_BUILD ?= sim_build_$(MODULE)
|
||||
|
||||
ifeq ($(SIM), icarus)
|
||||
PLUSARGS += -fst
|
||||
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).AXIS_DATA_WIDTH=$(PARAM_AXIS_DATA_WIDTH)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).AXIS_KEEP_ENABLE=$(PARAM_AXIS_KEEP_ENABLE)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).AXIS_KEEP_WIDTH=$(PARAM_AXIS_KEEP_WIDTH)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).ENABLE_PADDING=$(PARAM_ENABLE_PADDING)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).MIN_FRAME_LENGTH=$(PARAM_MIN_FRAME_LENGTH)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).TX_FIFO_DEPTH=$(PARAM_TX_FIFO_DEPTH)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).TX_FRAME_FIFO=$(PARAM_TX_FRAME_FIFO)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).TX_DROP_BAD_FRAME=$(PARAM_TX_DROP_BAD_FRAME)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).TX_DROP_WHEN_FULL=$(PARAM_TX_DROP_WHEN_FULL)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).RX_FIFO_DEPTH=$(PARAM_RX_FIFO_DEPTH)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).RX_FRAME_FIFO=$(PARAM_RX_FRAME_FIFO)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).RX_DROP_BAD_FRAME=$(PARAM_RX_DROP_BAD_FRAME)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).RX_DROP_WHEN_FULL=$(PARAM_RX_DROP_WHEN_FULL)
|
||||
|
||||
ifeq ($(WAVES), 1)
|
||||
VERILOG_SOURCES += iverilog_dump.v
|
||||
COMPILE_ARGS += -s iverilog_dump
|
||||
endif
|
||||
else ifeq ($(SIM), verilator)
|
||||
COMPILE_ARGS += -Wno-SELRANGE -Wno-WIDTH
|
||||
|
||||
COMPILE_ARGS += -GAXIS_DATA_WIDTH=$(PARAM_AXIS_DATA_WIDTH)
|
||||
COMPILE_ARGS += -GAXIS_KEEP_ENABLE=$(PARAM_AXIS_KEEP_ENABLE)
|
||||
COMPILE_ARGS += -GAXIS_KEEP_WIDTH=$(PARAM_AXIS_KEEP_WIDTH)
|
||||
COMPILE_ARGS += -GENABLE_PADDING=$(PARAM_ENABLE_PADDING)
|
||||
COMPILE_ARGS += -GMIN_FRAME_LENGTH=$(PARAM_MIN_FRAME_LENGTH)
|
||||
COMPILE_ARGS += -GTX_FIFO_DEPTH=$(PARAM_TX_FIFO_DEPTH)
|
||||
COMPILE_ARGS += -GTX_FRAME_FIFO=$(PARAM_TX_FRAME_FIFO)
|
||||
COMPILE_ARGS += -GTX_DROP_BAD_FRAME=$(PARAM_TX_DROP_BAD_FRAME)
|
||||
COMPILE_ARGS += -GTX_DROP_WHEN_FULL=$(PARAM_TX_DROP_WHEN_FULL)
|
||||
COMPILE_ARGS += -GRX_FIFO_DEPTH=$(PARAM_RX_FIFO_DEPTH)
|
||||
COMPILE_ARGS += -GRX_FRAME_FIFO=$(PARAM_RX_FRAME_FIFO)
|
||||
COMPILE_ARGS += -GRX_DROP_BAD_FRAME=$(PARAM_RX_DROP_BAD_FRAME)
|
||||
COMPILE_ARGS += -GRX_DROP_WHEN_FULL=$(PARAM_RX_DROP_WHEN_FULL)
|
||||
|
||||
ifeq ($(WAVES), 1)
|
||||
COMPILE_ARGS += --trace-fst
|
||||
endif
|
||||
endif
|
||||
|
||||
iverilog_dump.v:
|
||||
echo 'module iverilog_dump();' > $@
|
||||
echo 'initial begin' >> $@
|
||||
echo ' $$dumpfile("$(TOPLEVEL).fst");' >> $@
|
||||
echo ' $$dumpvars(0, $(TOPLEVEL));' >> $@
|
||||
echo 'end' >> $@
|
||||
echo 'endmodule' >> $@
|
||||
|
||||
clean::
|
||||
@rm -rf sim_build_*
|
||||
@rm -rf iverilog_dump.v
|
||||
@rm -rf dump.fst $(TOPLEVEL).fst
|
||||
|
||||
include $(shell cocotb-config --makefiles)/Makefile.sim
|
||||
|
246
tb/eth_mac_1g_rgmii_fifo/test_eth_mac_1g_rgmii_fifo.py
Normal file
246
tb/eth_mac_1g_rgmii_fifo/test_eth_mac_1g_rgmii_fifo.py
Normal file
@ -0,0 +1,246 @@
|
||||
#!/usr/bin/env python
|
||||
"""
|
||||
|
||||
Copyright (c) 2020 Alex Forencich
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
"""
|
||||
|
||||
import itertools
|
||||
import logging
|
||||
import os
|
||||
|
||||
import cocotb_test.simulator
|
||||
|
||||
import cocotb
|
||||
from cocotb.clock import Clock
|
||||
from cocotb.triggers import RisingEdge, Timer
|
||||
from cocotb.regression import TestFactory
|
||||
|
||||
from cocotbext.eth import GmiiFrame, RgmiiPhy
|
||||
from cocotbext.axi import AxiStreamSource, AxiStreamSink
|
||||
|
||||
|
||||
class TB:
|
||||
def __init__(self, dut, speed=1000e6):
|
||||
self.dut = dut
|
||||
|
||||
self.log = logging.getLogger("cocotb.tb")
|
||||
self.log.setLevel(logging.DEBUG)
|
||||
|
||||
cocotb.fork(Clock(dut.logic_clk, 8, units="ns").start())
|
||||
|
||||
self.rgmii_phy = RgmiiPhy(dut.rgmii_txd, dut.rgmii_tx_ctl, dut.rgmii_tx_clk,
|
||||
dut.rgmii_rxd, dut.rgmii_rx_ctl, dut.rgmii_rx_clk, speed=speed)
|
||||
|
||||
self.axis_source = AxiStreamSource(dut, "tx_axis", dut.logic_clk, dut.logic_rst)
|
||||
self.axis_sink = AxiStreamSink(dut, "rx_axis", dut.logic_clk, dut.logic_rst)
|
||||
|
||||
dut.ifg_delay.setimmediatevalue(0)
|
||||
|
||||
dut.gtx_clk.setimmediatevalue(0)
|
||||
dut.gtx_clk90.setimmediatevalue(0)
|
||||
|
||||
cocotb.fork(self._run_gtx_clk())
|
||||
|
||||
async def reset(self):
|
||||
self.dut.gtx_rst.setimmediatevalue(0)
|
||||
self.dut.logic_rst.setimmediatevalue(0)
|
||||
await RisingEdge(self.dut.gtx_clk)
|
||||
await RisingEdge(self.dut.gtx_clk)
|
||||
self.dut.gtx_rst <= 1
|
||||
self.dut.logic_rst <= 1
|
||||
await RisingEdge(self.dut.gtx_clk)
|
||||
await RisingEdge(self.dut.gtx_clk)
|
||||
self.dut.gtx_rst <= 0
|
||||
self.dut.logic_rst <= 0
|
||||
await RisingEdge(self.dut.gtx_clk)
|
||||
await RisingEdge(self.dut.gtx_clk)
|
||||
|
||||
async def _run_gtx_clk(self):
|
||||
t = Timer(2, 'ns')
|
||||
while True:
|
||||
self.dut.gtx_clk <= 1
|
||||
await t
|
||||
self.dut.gtx_clk90 <= 1
|
||||
await t
|
||||
self.dut.gtx_clk <= 0
|
||||
await t
|
||||
self.dut.gtx_clk90 <= 0
|
||||
await t
|
||||
|
||||
|
||||
async def run_test_rx(dut, payload_lengths=None, payload_data=None, ifg=12, speed=1000e6):
|
||||
|
||||
tb = TB(dut, speed)
|
||||
|
||||
tb.rgmii_phy.rx.ifg = ifg
|
||||
tb.dut.ifg_delay <= ifg
|
||||
|
||||
await tb.reset()
|
||||
|
||||
for k in range(100):
|
||||
await RisingEdge(dut.rx_clk)
|
||||
|
||||
if speed == 10e6:
|
||||
assert dut.speed == 0
|
||||
elif speed == 100e6:
|
||||
assert dut.speed == 1
|
||||
else:
|
||||
assert dut.speed == 2
|
||||
|
||||
test_frames = [payload_data(x) for x in payload_lengths()]
|
||||
|
||||
for test_data in test_frames:
|
||||
test_frame = GmiiFrame.from_payload(test_data)
|
||||
await tb.rgmii_phy.rx.send(test_frame)
|
||||
|
||||
for test_data in test_frames:
|
||||
rx_frame = await tb.axis_sink.recv()
|
||||
|
||||
assert rx_frame.tdata == test_data
|
||||
assert rx_frame.tuser == 0
|
||||
|
||||
assert tb.axis_sink.empty()
|
||||
|
||||
await RisingEdge(dut.rx_clk)
|
||||
await RisingEdge(dut.rx_clk)
|
||||
|
||||
|
||||
async def run_test_tx(dut, payload_lengths=None, payload_data=None, ifg=12, speed=1000e6):
|
||||
|
||||
tb = TB(dut, speed)
|
||||
|
||||
tb.rgmii_phy.rx.ifg = ifg
|
||||
tb.dut.ifg_delay <= ifg
|
||||
|
||||
await tb.reset()
|
||||
|
||||
for k in range(100):
|
||||
await RisingEdge(dut.rx_clk)
|
||||
|
||||
if speed == 10e6:
|
||||
assert dut.speed == 0
|
||||
elif speed == 100e6:
|
||||
assert dut.speed == 1
|
||||
else:
|
||||
assert dut.speed == 2
|
||||
|
||||
test_frames = [payload_data(x) for x in payload_lengths()]
|
||||
|
||||
for test_data in test_frames:
|
||||
await tb.axis_source.send(test_data)
|
||||
|
||||
for test_data in test_frames:
|
||||
rx_frame = await tb.rgmii_phy.tx.recv()
|
||||
|
||||
assert rx_frame.get_payload() == test_data
|
||||
assert rx_frame.check_fcs()
|
||||
assert rx_frame.error is None
|
||||
|
||||
assert tb.rgmii_phy.tx.empty()
|
||||
|
||||
await RisingEdge(dut.tx_clk)
|
||||
await RisingEdge(dut.tx_clk)
|
||||
|
||||
|
||||
def size_list():
|
||||
return list(range(60, 128)) + [512, 1514] + [60]*10
|
||||
|
||||
|
||||
def incrementing_payload(length):
|
||||
return bytearray(itertools.islice(itertools.cycle(range(256)), length))
|
||||
|
||||
|
||||
def cycle_en():
|
||||
return itertools.cycle([0, 0, 0, 1])
|
||||
|
||||
|
||||
if cocotb.SIM_NAME:
|
||||
|
||||
for test in [run_test_rx, run_test_tx]:
|
||||
|
||||
factory = TestFactory(test)
|
||||
factory.add_option("payload_lengths", [size_list])
|
||||
factory.add_option("payload_data", [incrementing_payload])
|
||||
factory.add_option("ifg", [12])
|
||||
factory.add_option("speed", [1000e6, 100e6, 10e6])
|
||||
factory.generate_tests()
|
||||
|
||||
|
||||
# cocotb-test
|
||||
|
||||
tests_dir = os.path.abspath(os.path.dirname(__file__))
|
||||
rtl_dir = os.path.abspath(os.path.join(tests_dir, '..', '..', 'rtl'))
|
||||
lib_dir = os.path.abspath(os.path.join(rtl_dir, '..', 'lib'))
|
||||
axis_rtl_dir = os.path.abspath(os.path.join(lib_dir, 'axis', 'rtl'))
|
||||
|
||||
|
||||
def test_eth_mac_1g_rgmii_fifo(request):
|
||||
dut = "eth_mac_1g_rgmii_fifo"
|
||||
module = os.path.splitext(os.path.basename(__file__))[0]
|
||||
toplevel = dut
|
||||
|
||||
verilog_sources = [
|
||||
os.path.join(rtl_dir, f"{dut}.v"),
|
||||
os.path.join(rtl_dir, "eth_mac_1g_rgmii.v"),
|
||||
os.path.join(rtl_dir, "iddr.v"),
|
||||
os.path.join(rtl_dir, "oddr.v"),
|
||||
os.path.join(rtl_dir, "ssio_ddr_in.v"),
|
||||
os.path.join(rtl_dir, "rgmii_phy_if.v"),
|
||||
os.path.join(rtl_dir, "eth_mac_1g.v"),
|
||||
os.path.join(rtl_dir, "axis_gmii_rx.v"),
|
||||
os.path.join(rtl_dir, "axis_gmii_tx.v"),
|
||||
os.path.join(rtl_dir, "lfsr.v"),
|
||||
os.path.join(axis_rtl_dir, "axis_adapter.v"),
|
||||
os.path.join(axis_rtl_dir, "axis_async_fifo.v"),
|
||||
os.path.join(axis_rtl_dir, "axis_async_fifo_adapter.v"),
|
||||
]
|
||||
|
||||
parameters = {}
|
||||
|
||||
parameters['AXIS_DATA_WIDTH'] = 8
|
||||
parameters['AXIS_KEEP_ENABLE'] = int(parameters['AXIS_DATA_WIDTH'] > 8)
|
||||
parameters['AXIS_KEEP_WIDTH'] = parameters['AXIS_DATA_WIDTH'] // 8
|
||||
parameters['ENABLE_PADDING'] = 1
|
||||
parameters['MIN_FRAME_LENGTH'] = 64
|
||||
parameters['TX_FIFO_DEPTH'] = 16384
|
||||
parameters['TX_FRAME_FIFO'] = 1
|
||||
parameters['TX_DROP_BAD_FRAME'] = parameters['TX_FRAME_FIFO']
|
||||
parameters['TX_DROP_WHEN_FULL'] = 0
|
||||
parameters['RX_FIFO_DEPTH'] = 16384
|
||||
parameters['RX_FRAME_FIFO'] = 1
|
||||
parameters['RX_DROP_BAD_FRAME'] = parameters['RX_FRAME_FIFO']
|
||||
parameters['RX_DROP_WHEN_FULL'] = parameters['RX_FRAME_FIFO']
|
||||
|
||||
extra_env = {f'PARAM_{k}': str(v) for k, v in parameters.items()}
|
||||
|
||||
sim_build = os.path.join(tests_dir,
|
||||
"sim_build_"+request.node.name.replace('[', '-').replace(']', ''))
|
||||
|
||||
cocotb_test.simulator.run(
|
||||
python_search=[tests_dir],
|
||||
verilog_sources=verilog_sources,
|
||||
toplevel=toplevel,
|
||||
module=module,
|
||||
parameters=parameters,
|
||||
sim_build=sim_build,
|
||||
extra_env=extra_env,
|
||||
)
|
81
tb/eth_mac_mii/Makefile
Normal file
81
tb/eth_mac_mii/Makefile
Normal file
@ -0,0 +1,81 @@
|
||||
# Copyright (c) 2020 Alex Forencich
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
|
||||
TOPLEVEL_LANG = verilog
|
||||
|
||||
SIM ?= icarus
|
||||
WAVES ?= 0
|
||||
|
||||
COCOTB_HDL_TIMEUNIT = 1ns
|
||||
COCOTB_HDL_TIMEPRECISION = 1ps
|
||||
|
||||
DUT = eth_mac_mii
|
||||
TOPLEVEL = $(DUT)
|
||||
MODULE = test_$(DUT)
|
||||
VERILOG_SOURCES += ../../rtl/$(DUT).v
|
||||
VERILOG_SOURCES += ../../rtl/ssio_sdr_in.v
|
||||
VERILOG_SOURCES += ../../rtl/mii_phy_if.v
|
||||
VERILOG_SOURCES += ../../rtl/eth_mac_1g.v
|
||||
VERILOG_SOURCES += ../../rtl/axis_gmii_rx.v
|
||||
VERILOG_SOURCES += ../../rtl/axis_gmii_tx.v
|
||||
VERILOG_SOURCES += ../../rtl/lfsr.v
|
||||
|
||||
# module parameters
|
||||
export PARAM_ENABLE_PADDING ?= 1
|
||||
export PARAM_MIN_FRAME_LENGTH ?= 64
|
||||
|
||||
SIM_BUILD ?= sim_build_$(MODULE)
|
||||
|
||||
ifeq ($(SIM), icarus)
|
||||
PLUSARGS += -fst
|
||||
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).ENABLE_PADDING=$(PARAM_ENABLE_PADDING)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).MIN_FRAME_LENGTH=$(PARAM_MIN_FRAME_LENGTH)
|
||||
|
||||
ifeq ($(WAVES), 1)
|
||||
VERILOG_SOURCES += iverilog_dump.v
|
||||
COMPILE_ARGS += -s iverilog_dump
|
||||
endif
|
||||
else ifeq ($(SIM), verilator)
|
||||
COMPILE_ARGS += -Wno-SELRANGE -Wno-WIDTH
|
||||
|
||||
COMPILE_ARGS += -GENABLE_PADDING=$(PARAM_ENABLE_PADDING)
|
||||
COMPILE_ARGS += -GMIN_FRAME_LENGTH=$(PARAM_MIN_FRAME_LENGTH)
|
||||
|
||||
ifeq ($(WAVES), 1)
|
||||
COMPILE_ARGS += --trace-fst
|
||||
endif
|
||||
endif
|
||||
|
||||
iverilog_dump.v:
|
||||
echo 'module iverilog_dump();' > $@
|
||||
echo 'initial begin' >> $@
|
||||
echo ' $$dumpfile("$(TOPLEVEL).fst");' >> $@
|
||||
echo ' $$dumpvars(0, $(TOPLEVEL));' >> $@
|
||||
echo 'end' >> $@
|
||||
echo 'endmodule' >> $@
|
||||
|
||||
clean::
|
||||
@rm -rf sim_build_*
|
||||
@rm -rf iverilog_dump.v
|
||||
@rm -rf dump.fst $(TOPLEVEL).fst
|
||||
|
||||
include $(shell cocotb-config --makefiles)/Makefile.sim
|
||||
|
186
tb/eth_mac_mii/test_eth_mac_mii.py
Normal file
186
tb/eth_mac_mii/test_eth_mac_mii.py
Normal file
@ -0,0 +1,186 @@
|
||||
#!/usr/bin/env python
|
||||
"""
|
||||
|
||||
Copyright (c) 2020 Alex Forencich
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
"""
|
||||
|
||||
import itertools
|
||||
import logging
|
||||
import os
|
||||
|
||||
import cocotb_test.simulator
|
||||
|
||||
import cocotb
|
||||
from cocotb.triggers import RisingEdge
|
||||
from cocotb.regression import TestFactory
|
||||
|
||||
from cocotbext.eth import GmiiFrame, MiiPhy
|
||||
from cocotbext.axi import AxiStreamSource, AxiStreamSink
|
||||
|
||||
|
||||
class TB:
|
||||
def __init__(self, dut, speed=100e6):
|
||||
self.dut = dut
|
||||
|
||||
self.log = logging.getLogger("cocotb.tb")
|
||||
self.log.setLevel(logging.DEBUG)
|
||||
|
||||
self.mii_phy = MiiPhy(dut.mii_txd, dut.mii_tx_er, dut.mii_tx_en, dut.mii_tx_clk,
|
||||
dut.mii_rxd, dut.mii_rx_er, dut.mii_rx_dv, dut.mii_rx_clk, speed=speed)
|
||||
|
||||
self.axis_source = AxiStreamSource(dut, "tx_axis", dut.tx_clk, dut.tx_rst)
|
||||
self.axis_sink = AxiStreamSink(dut, "rx_axis", dut.rx_clk, dut.rx_rst)
|
||||
|
||||
dut.ifg_delay.setimmediatevalue(0)
|
||||
|
||||
async def reset(self):
|
||||
self.dut.rst.setimmediatevalue(0)
|
||||
await RisingEdge(self.dut.tx_clk)
|
||||
await RisingEdge(self.dut.tx_clk)
|
||||
self.dut.rst <= 1
|
||||
await RisingEdge(self.dut.tx_clk)
|
||||
await RisingEdge(self.dut.tx_clk)
|
||||
self.dut.rst <= 0
|
||||
await RisingEdge(self.dut.tx_clk)
|
||||
await RisingEdge(self.dut.tx_clk)
|
||||
|
||||
|
||||
async def run_test_rx(dut, payload_lengths=None, payload_data=None, ifg=12, speed=100e6):
|
||||
|
||||
tb = TB(dut, speed)
|
||||
|
||||
tb.mii_phy.rx.ifg = ifg
|
||||
tb.dut.ifg_delay <= ifg
|
||||
|
||||
await tb.reset()
|
||||
|
||||
test_frames = [payload_data(x) for x in payload_lengths()]
|
||||
|
||||
for test_data in test_frames:
|
||||
test_frame = GmiiFrame.from_payload(test_data)
|
||||
await tb.mii_phy.rx.send(test_frame)
|
||||
|
||||
for test_data in test_frames:
|
||||
rx_frame = await tb.axis_sink.recv()
|
||||
|
||||
assert rx_frame.tdata == test_data
|
||||
assert rx_frame.tuser == 0
|
||||
|
||||
assert tb.axis_sink.empty()
|
||||
|
||||
await RisingEdge(dut.rx_clk)
|
||||
await RisingEdge(dut.rx_clk)
|
||||
|
||||
|
||||
async def run_test_tx(dut, payload_lengths=None, payload_data=None, ifg=12, speed=100e6):
|
||||
|
||||
tb = TB(dut, speed)
|
||||
|
||||
tb.mii_phy.rx.ifg = ifg
|
||||
tb.dut.ifg_delay <= ifg
|
||||
|
||||
await tb.reset()
|
||||
|
||||
test_frames = [payload_data(x) for x in payload_lengths()]
|
||||
|
||||
for test_data in test_frames:
|
||||
await tb.axis_source.send(test_data)
|
||||
|
||||
for test_data in test_frames:
|
||||
rx_frame = await tb.mii_phy.tx.recv()
|
||||
|
||||
assert rx_frame.get_payload() == test_data
|
||||
assert rx_frame.check_fcs()
|
||||
assert rx_frame.error is None
|
||||
|
||||
assert tb.mii_phy.tx.empty()
|
||||
|
||||
await RisingEdge(dut.tx_clk)
|
||||
await RisingEdge(dut.tx_clk)
|
||||
|
||||
|
||||
def size_list():
|
||||
return list(range(60, 128)) + [512, 1514] + [60]*10
|
||||
|
||||
|
||||
def incrementing_payload(length):
|
||||
return bytearray(itertools.islice(itertools.cycle(range(256)), length))
|
||||
|
||||
|
||||
def cycle_en():
|
||||
return itertools.cycle([0, 0, 0, 1])
|
||||
|
||||
|
||||
if cocotb.SIM_NAME:
|
||||
|
||||
for test in [run_test_rx, run_test_tx]:
|
||||
|
||||
factory = TestFactory(test)
|
||||
factory.add_option("payload_lengths", [size_list])
|
||||
factory.add_option("payload_data", [incrementing_payload])
|
||||
factory.add_option("ifg", [12])
|
||||
factory.add_option("speed", [100e6, 10e6])
|
||||
factory.generate_tests()
|
||||
|
||||
|
||||
# cocotb-test
|
||||
|
||||
tests_dir = os.path.abspath(os.path.dirname(__file__))
|
||||
rtl_dir = os.path.abspath(os.path.join(tests_dir, '..', '..', 'rtl'))
|
||||
lib_dir = os.path.abspath(os.path.join(rtl_dir, '..', 'lib'))
|
||||
axis_rtl_dir = os.path.abspath(os.path.join(lib_dir, 'axis', 'rtl'))
|
||||
|
||||
|
||||
def test_eth_mac_mii(request):
|
||||
dut = "eth_mac_mii"
|
||||
module = os.path.splitext(os.path.basename(__file__))[0]
|
||||
toplevel = dut
|
||||
|
||||
verilog_sources = [
|
||||
os.path.join(rtl_dir, f"{dut}.v"),
|
||||
os.path.join(rtl_dir, "ssio_sdr_in.v"),
|
||||
os.path.join(rtl_dir, "mii_phy_if.v"),
|
||||
os.path.join(rtl_dir, "eth_mac_1g.v"),
|
||||
os.path.join(rtl_dir, "axis_gmii_rx.v"),
|
||||
os.path.join(rtl_dir, "axis_gmii_tx.v"),
|
||||
os.path.join(rtl_dir, "lfsr.v"),
|
||||
]
|
||||
|
||||
parameters = {}
|
||||
|
||||
parameters['ENABLE_PADDING'] = 1
|
||||
parameters['MIN_FRAME_LENGTH'] = 64
|
||||
|
||||
extra_env = {f'PARAM_{k}': str(v) for k, v in parameters.items()}
|
||||
|
||||
sim_build = os.path.join(tests_dir,
|
||||
"sim_build_"+request.node.name.replace('[', '-').replace(']', ''))
|
||||
|
||||
cocotb_test.simulator.run(
|
||||
python_search=[tests_dir],
|
||||
verilog_sources=verilog_sources,
|
||||
toplevel=toplevel,
|
||||
module=module,
|
||||
parameters=parameters,
|
||||
sim_build=sim_build,
|
||||
extra_env=extra_env,
|
||||
)
|
118
tb/eth_mac_mii_fifo/Makefile
Normal file
118
tb/eth_mac_mii_fifo/Makefile
Normal file
@ -0,0 +1,118 @@
|
||||
# Copyright (c) 2020 Alex Forencich
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
|
||||
TOPLEVEL_LANG = verilog
|
||||
|
||||
SIM ?= icarus
|
||||
WAVES ?= 0
|
||||
|
||||
COCOTB_HDL_TIMEUNIT = 1ns
|
||||
COCOTB_HDL_TIMEPRECISION = 1ps
|
||||
|
||||
DUT = eth_mac_mii_fifo
|
||||
TOPLEVEL = $(DUT)
|
||||
MODULE = test_$(DUT)
|
||||
VERILOG_SOURCES += ../../rtl/$(DUT).v
|
||||
VERILOG_SOURCES += ../../rtl/eth_mac_mii.v
|
||||
VERILOG_SOURCES += ../../rtl/ssio_sdr_in.v
|
||||
VERILOG_SOURCES += ../../rtl/mii_phy_if.v
|
||||
VERILOG_SOURCES += ../../rtl/eth_mac_1g.v
|
||||
VERILOG_SOURCES += ../../rtl/axis_gmii_rx.v
|
||||
VERILOG_SOURCES += ../../rtl/axis_gmii_tx.v
|
||||
VERILOG_SOURCES += ../../rtl/lfsr.v
|
||||
VERILOG_SOURCES += ../../lib/axis/rtl/axis_adapter.v
|
||||
VERILOG_SOURCES += ../../lib/axis/rtl/axis_async_fifo.v
|
||||
VERILOG_SOURCES += ../../lib/axis/rtl/axis_async_fifo_adapter.v
|
||||
|
||||
# module parameters
|
||||
export PARAM_AXIS_DATA_WIDTH ?= 8
|
||||
export PARAM_AXIS_KEEP_ENABLE ?= $(shell expr $(PARAM_AXIS_DATA_WIDTH) \> 8 )
|
||||
export PARAM_AXIS_KEEP_WIDTH ?= $(shell expr $(PARAM_AXIS_DATA_WIDTH) / 8 )
|
||||
export PARAM_ENABLE_PADDING ?= 1
|
||||
export PARAM_MIN_FRAME_LENGTH ?= 64
|
||||
export PARAM_TX_FIFO_DEPTH ?= 16384
|
||||
export PARAM_TX_FRAME_FIFO ?= 1
|
||||
export PARAM_TX_DROP_BAD_FRAME ?= $(PARAM_TX_FRAME_FIFO)
|
||||
export PARAM_TX_DROP_WHEN_FULL ?= 0
|
||||
export PARAM_RX_FIFO_DEPTH ?= 16384
|
||||
export PARAM_RX_FRAME_FIFO ?= 1
|
||||
export PARAM_RX_DROP_BAD_FRAME ?= $(PARAM_RX_FRAME_FIFO)
|
||||
export PARAM_RX_DROP_WHEN_FULL ?= $(PARAM_RX_FRAME_FIFO)
|
||||
|
||||
SIM_BUILD ?= sim_build_$(MODULE)
|
||||
|
||||
ifeq ($(SIM), icarus)
|
||||
PLUSARGS += -fst
|
||||
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).AXIS_DATA_WIDTH=$(PARAM_AXIS_DATA_WIDTH)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).AXIS_KEEP_ENABLE=$(PARAM_AXIS_KEEP_ENABLE)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).AXIS_KEEP_WIDTH=$(PARAM_AXIS_KEEP_WIDTH)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).ENABLE_PADDING=$(PARAM_ENABLE_PADDING)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).MIN_FRAME_LENGTH=$(PARAM_MIN_FRAME_LENGTH)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).TX_FIFO_DEPTH=$(PARAM_TX_FIFO_DEPTH)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).TX_FRAME_FIFO=$(PARAM_TX_FRAME_FIFO)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).TX_DROP_BAD_FRAME=$(PARAM_TX_DROP_BAD_FRAME)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).TX_DROP_WHEN_FULL=$(PARAM_TX_DROP_WHEN_FULL)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).RX_FIFO_DEPTH=$(PARAM_RX_FIFO_DEPTH)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).RX_FRAME_FIFO=$(PARAM_RX_FRAME_FIFO)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).RX_DROP_BAD_FRAME=$(PARAM_RX_DROP_BAD_FRAME)
|
||||
COMPILE_ARGS += -P $(TOPLEVEL).RX_DROP_WHEN_FULL=$(PARAM_RX_DROP_WHEN_FULL)
|
||||
|
||||
ifeq ($(WAVES), 1)
|
||||
VERILOG_SOURCES += iverilog_dump.v
|
||||
COMPILE_ARGS += -s iverilog_dump
|
||||
endif
|
||||
else ifeq ($(SIM), verilator)
|
||||
COMPILE_ARGS += -Wno-SELRANGE -Wno-WIDTH
|
||||
|
||||
COMPILE_ARGS += -GAXIS_DATA_WIDTH=$(PARAM_AXIS_DATA_WIDTH)
|
||||
COMPILE_ARGS += -GAXIS_KEEP_ENABLE=$(PARAM_AXIS_KEEP_ENABLE)
|
||||
COMPILE_ARGS += -GAXIS_KEEP_WIDTH=$(PARAM_AXIS_KEEP_WIDTH)
|
||||
COMPILE_ARGS += -GENABLE_PADDING=$(PARAM_ENABLE_PADDING)
|
||||
COMPILE_ARGS += -GMIN_FRAME_LENGTH=$(PARAM_MIN_FRAME_LENGTH)
|
||||
COMPILE_ARGS += -GTX_FIFO_DEPTH=$(PARAM_TX_FIFO_DEPTH)
|
||||
COMPILE_ARGS += -GTX_FRAME_FIFO=$(PARAM_TX_FRAME_FIFO)
|
||||
COMPILE_ARGS += -GTX_DROP_BAD_FRAME=$(PARAM_TX_DROP_BAD_FRAME)
|
||||
COMPILE_ARGS += -GTX_DROP_WHEN_FULL=$(PARAM_TX_DROP_WHEN_FULL)
|
||||
COMPILE_ARGS += -GRX_FIFO_DEPTH=$(PARAM_RX_FIFO_DEPTH)
|
||||
COMPILE_ARGS += -GRX_FRAME_FIFO=$(PARAM_RX_FRAME_FIFO)
|
||||
COMPILE_ARGS += -GRX_DROP_BAD_FRAME=$(PARAM_RX_DROP_BAD_FRAME)
|
||||
COMPILE_ARGS += -GRX_DROP_WHEN_FULL=$(PARAM_RX_DROP_WHEN_FULL)
|
||||
|
||||
ifeq ($(WAVES), 1)
|
||||
COMPILE_ARGS += --trace-fst
|
||||
endif
|
||||
endif
|
||||
|
||||
iverilog_dump.v:
|
||||
echo 'module iverilog_dump();' > $@
|
||||
echo 'initial begin' >> $@
|
||||
echo ' $$dumpfile("$(TOPLEVEL).fst");' >> $@
|
||||
echo ' $$dumpvars(0, $(TOPLEVEL));' >> $@
|
||||
echo 'end' >> $@
|
||||
echo 'endmodule' >> $@
|
||||
|
||||
clean::
|
||||
@rm -rf sim_build_*
|
||||
@rm -rf iverilog_dump.v
|
||||
@rm -rf dump.fst $(TOPLEVEL).fst
|
||||
|
||||
include $(shell cocotb-config --makefiles)/Makefile.sim
|
||||
|
204
tb/eth_mac_mii_fifo/test_eth_mac_mii_fifo.py
Normal file
204
tb/eth_mac_mii_fifo/test_eth_mac_mii_fifo.py
Normal file
@ -0,0 +1,204 @@
|
||||
#!/usr/bin/env python
|
||||
"""
|
||||
|
||||
Copyright (c) 2020 Alex Forencich
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
"""
|
||||
|
||||
import itertools
|
||||
import logging
|
||||
import os
|
||||
|
||||
import cocotb_test.simulator
|
||||
|
||||
import cocotb
|
||||
from cocotb.clock import Clock
|
||||
from cocotb.triggers import RisingEdge
|
||||
from cocotb.regression import TestFactory
|
||||
|
||||
from cocotbext.eth import GmiiFrame, MiiPhy
|
||||
from cocotbext.axi import AxiStreamSource, AxiStreamSink
|
||||
|
||||
|
||||
class TB:
|
||||
def __init__(self, dut, speed=100e6):
|
||||
self.dut = dut
|
||||
|
||||
self.log = logging.getLogger("cocotb.tb")
|
||||
self.log.setLevel(logging.DEBUG)
|
||||
|
||||
cocotb.fork(Clock(dut.logic_clk, 40, units="ns").start())
|
||||
|
||||
self.mii_phy = MiiPhy(dut.mii_txd, dut.mii_tx_er, dut.mii_tx_en, dut.mii_tx_clk,
|
||||
dut.mii_rxd, dut.mii_rx_er, dut.mii_rx_dv, dut.mii_rx_clk, speed=speed)
|
||||
|
||||
self.axis_source = AxiStreamSource(dut, "tx_axis", dut.logic_clk, dut.logic_rst)
|
||||
self.axis_sink = AxiStreamSink(dut, "rx_axis", dut.logic_clk, dut.logic_rst)
|
||||
|
||||
dut.ifg_delay.setimmediatevalue(0)
|
||||
|
||||
async def reset(self):
|
||||
self.dut.logic_rst.setimmediatevalue(0)
|
||||
for k in range(10):
|
||||
await RisingEdge(self.dut.logic_clk)
|
||||
self.dut.logic_rst <= 1
|
||||
for k in range(10):
|
||||
await RisingEdge(self.dut.logic_clk)
|
||||
self.dut.logic_rst <= 0
|
||||
for k in range(10):
|
||||
await RisingEdge(self.dut.logic_clk)
|
||||
|
||||
|
||||
async def run_test_rx(dut, payload_lengths=None, payload_data=None, ifg=12, speed=100e6):
|
||||
|
||||
tb = TB(dut, speed)
|
||||
|
||||
tb.mii_phy.rx.ifg = ifg
|
||||
tb.dut.ifg_delay <= ifg
|
||||
|
||||
await tb.reset()
|
||||
|
||||
test_frames = [payload_data(x) for x in payload_lengths()]
|
||||
|
||||
for test_data in test_frames:
|
||||
test_frame = GmiiFrame.from_payload(test_data)
|
||||
await tb.mii_phy.rx.send(test_frame)
|
||||
|
||||
for test_data in test_frames:
|
||||
rx_frame = await tb.axis_sink.recv()
|
||||
|
||||
assert rx_frame.tdata == test_data
|
||||
assert rx_frame.tuser == 0
|
||||
|
||||
assert tb.axis_sink.empty()
|
||||
|
||||
await RisingEdge(dut.logic_clk)
|
||||
await RisingEdge(dut.logic_clk)
|
||||
|
||||
|
||||
async def run_test_tx(dut, payload_lengths=None, payload_data=None, ifg=12, speed=100e6):
|
||||
|
||||
tb = TB(dut, speed)
|
||||
|
||||
tb.mii_phy.rx.ifg = ifg
|
||||
tb.dut.ifg_delay <= ifg
|
||||
|
||||
await tb.reset()
|
||||
|
||||
test_frames = [payload_data(x) for x in payload_lengths()]
|
||||
|
||||
for test_data in test_frames:
|
||||
await tb.axis_source.send(test_data)
|
||||
|
||||
for test_data in test_frames:
|
||||
rx_frame = await tb.mii_phy.tx.recv()
|
||||
|
||||
assert rx_frame.get_payload() == test_data
|
||||
assert rx_frame.check_fcs()
|
||||
assert rx_frame.error is None
|
||||
|
||||
assert tb.mii_phy.tx.empty()
|
||||
|
||||
await RisingEdge(dut.logic_clk)
|
||||
await RisingEdge(dut.logic_clk)
|
||||
|
||||
|
||||
def size_list():
|
||||
return list(range(60, 128)) + [512, 1514] + [60]*10
|
||||
|
||||
|
||||
def incrementing_payload(length):
|
||||
return bytearray(itertools.islice(itertools.cycle(range(256)), length))
|
||||
|
||||
|
||||
def cycle_en():
|
||||
return itertools.cycle([0, 0, 0, 1])
|
||||
|
||||
|
||||
if cocotb.SIM_NAME:
|
||||
|
||||
for test in [run_test_rx, run_test_tx]:
|
||||
|
||||
factory = TestFactory(test)
|
||||
factory.add_option("payload_lengths", [size_list])
|
||||
factory.add_option("payload_data", [incrementing_payload])
|
||||
factory.add_option("ifg", [12])
|
||||
factory.add_option("speed", [100e6, 10e6])
|
||||
factory.generate_tests()
|
||||
|
||||
|
||||
# cocotb-test
|
||||
|
||||
tests_dir = os.path.abspath(os.path.dirname(__file__))
|
||||
rtl_dir = os.path.abspath(os.path.join(tests_dir, '..', '..', 'rtl'))
|
||||
lib_dir = os.path.abspath(os.path.join(rtl_dir, '..', 'lib'))
|
||||
axis_rtl_dir = os.path.abspath(os.path.join(lib_dir, 'axis', 'rtl'))
|
||||
|
||||
|
||||
def test_eth_mac_mii_fifo(request):
|
||||
dut = "eth_mac_mii_fifo"
|
||||
module = os.path.splitext(os.path.basename(__file__))[0]
|
||||
toplevel = dut
|
||||
|
||||
verilog_sources = [
|
||||
os.path.join(rtl_dir, f"{dut}.v"),
|
||||
os.path.join(rtl_dir, "eth_mac_mii.v"),
|
||||
os.path.join(rtl_dir, "ssio_sdr_in.v"),
|
||||
os.path.join(rtl_dir, "mii_phy_if.v"),
|
||||
os.path.join(rtl_dir, "eth_mac_1g.v"),
|
||||
os.path.join(rtl_dir, "axis_gmii_rx.v"),
|
||||
os.path.join(rtl_dir, "axis_gmii_tx.v"),
|
||||
os.path.join(rtl_dir, "lfsr.v"),
|
||||
os.path.join(axis_rtl_dir, "axis_adapter.v"),
|
||||
os.path.join(axis_rtl_dir, "axis_async_fifo.v"),
|
||||
os.path.join(axis_rtl_dir, "axis_async_fifo_adapter.v"),
|
||||
]
|
||||
|
||||
parameters = {}
|
||||
|
||||
parameters['AXIS_DATA_WIDTH'] = 8
|
||||
parameters['AXIS_KEEP_ENABLE'] = int(parameters['AXIS_DATA_WIDTH'] > 8)
|
||||
parameters['AXIS_KEEP_WIDTH'] = parameters['AXIS_DATA_WIDTH'] // 8
|
||||
parameters['ENABLE_PADDING'] = 1
|
||||
parameters['MIN_FRAME_LENGTH'] = 64
|
||||
parameters['TX_FIFO_DEPTH'] = 16384
|
||||
parameters['TX_FRAME_FIFO'] = 1
|
||||
parameters['TX_DROP_BAD_FRAME'] = parameters['TX_FRAME_FIFO']
|
||||
parameters['TX_DROP_WHEN_FULL'] = 0
|
||||
parameters['RX_FIFO_DEPTH'] = 16384
|
||||
parameters['RX_FRAME_FIFO'] = 1
|
||||
parameters['RX_DROP_BAD_FRAME'] = parameters['RX_FRAME_FIFO']
|
||||
parameters['RX_DROP_WHEN_FULL'] = parameters['RX_FRAME_FIFO']
|
||||
|
||||
extra_env = {f'PARAM_{k}': str(v) for k, v in parameters.items()}
|
||||
|
||||
sim_build = os.path.join(tests_dir,
|
||||
"sim_build_"+request.node.name.replace('[', '-').replace(']', ''))
|
||||
|
||||
cocotb_test.simulator.run(
|
||||
python_search=[tests_dir],
|
||||
verilog_sources=verilog_sources,
|
||||
toplevel=toplevel,
|
||||
module=module,
|
||||
parameters=parameters,
|
||||
sim_build=sim_build,
|
||||
extra_env=extra_env,
|
||||
)
|
Loading…
x
Reference in New Issue
Block a user