From 7117de682ad29fb6906aa3f392234a3d0f2280d3 Mon Sep 17 00:00:00 2001 From: Alex Forencich Date: Tue, 29 Dec 2020 22:02:27 -0800 Subject: [PATCH] Add cocotb testbench for ptp_perout --- tb/ptp_perout/Makefile | 95 ++++++++++++++++++ tb/ptp_perout/test_ptp_perout.py | 165 +++++++++++++++++++++++++++++++ 2 files changed, 260 insertions(+) create mode 100644 tb/ptp_perout/Makefile create mode 100644 tb/ptp_perout/test_ptp_perout.py diff --git a/tb/ptp_perout/Makefile b/tb/ptp_perout/Makefile new file mode 100644 index 000000000..7a6f15cb3 --- /dev/null +++ b/tb/ptp_perout/Makefile @@ -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 = ptp_perout +TOPLEVEL = $(DUT) +MODULE = test_$(DUT) +VERILOG_SOURCES += ../../rtl/$(DUT).v + +# module parameters +export PARAM_FNS_ENABLE ?= 1 +export PARAM_OUT_START_S ?= 0 +export PARAM_OUT_START_NS ?= 0 +export PARAM_OUT_START_FNS ?= 0 +export PARAM_OUT_PERIOD_S ?= 1 +export PARAM_OUT_PERIOD_NS ?= 0 +export PARAM_OUT_PERIOD_FNS ?= 0 +export PARAM_OUT_WIDTH_S ?= 0 +export PARAM_OUT_WIDTH_NS ?= 1000 +export PARAM_OUT_WIDTH_FNS ?= 0 + +ifeq ($(SIM), icarus) + PLUSARGS += -fst + + COMPILE_ARGS += -P $(TOPLEVEL).FNS_ENABLE=$(PARAM_FNS_ENABLE) + COMPILE_ARGS += -P $(TOPLEVEL).OUT_START_S=$(PARAM_OUT_START_S) + COMPILE_ARGS += -P $(TOPLEVEL).OUT_START_NS=$(PARAM_OUT_START_NS) + COMPILE_ARGS += -P $(TOPLEVEL).OUT_START_FNS=$(PARAM_OUT_START_FNS) + COMPILE_ARGS += -P $(TOPLEVEL).OUT_PERIOD_S=$(PARAM_OUT_PERIOD_S) + COMPILE_ARGS += -P $(TOPLEVEL).OUT_PERIOD_NS=$(PARAM_OUT_PERIOD_NS) + COMPILE_ARGS += -P $(TOPLEVEL).OUT_PERIOD_FNS=$(PARAM_OUT_PERIOD_FNS) + COMPILE_ARGS += -P $(TOPLEVEL).OUT_WIDTH_S=$(PARAM_OUT_WIDTH_S) + COMPILE_ARGS += -P $(TOPLEVEL).OUT_WIDTH_NS=$(PARAM_OUT_WIDTH_NS) + COMPILE_ARGS += -P $(TOPLEVEL).OUT_WIDTH_FNS=$(PARAM_OUT_WIDTH_FNS) + + 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 += -GFNS_ENABLE=$(PARAM_FNS_ENABLE) + COMPILE_ARGS += -GOUT_START_S=$(PARAM_OUT_START_S) + COMPILE_ARGS += -GOUT_START_NS=$(PARAM_OUT_START_NS) + COMPILE_ARGS += -GOUT_START_FNS=$(PARAM_OUT_START_FNS) + COMPILE_ARGS += -GOUT_PERIOD_S=$(PARAM_OUT_PERIOD_S) + COMPILE_ARGS += -GOUT_PERIOD_NS=$(PARAM_OUT_PERIOD_NS) + COMPILE_ARGS += -GOUT_PERIOD_FNS=$(PARAM_OUT_PERIOD_FNS) + COMPILE_ARGS += -GOUT_WIDTH_S=$(PARAM_OUT_WIDTH_S) + COMPILE_ARGS += -GOUT_WIDTH_NS=$(PARAM_OUT_WIDTH_NS) + COMPILE_ARGS += -GOUT_WIDTH_FNS=$(PARAM_OUT_WIDTH_FNS) + + ifeq ($(WAVES), 1) + COMPILE_ARGS += --trace-fst + endif +endif + +include $(shell cocotb-config --makefiles)/Makefile.sim + +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 iverilog_dump.v + @rm -rf dump.fst $(TOPLEVEL).fst diff --git a/tb/ptp_perout/test_ptp_perout.py b/tb/ptp_perout/test_ptp_perout.py new file mode 100644 index 000000000..f16340002 --- /dev/null +++ b/tb/ptp_perout/test_ptp_perout.py @@ -0,0 +1,165 @@ +#!/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 logging +import os + +import cocotb_test.simulator + +import cocotb +from cocotb.clock import Clock +from cocotb.triggers import RisingEdge, Timer + +from cocotbext.eth import PtpClock + + +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.ptp_clock = PtpClock( + ts_96=dut.input_ts_96, + ts_step=dut.input_ts_step, + clock=dut.clk, + reset=dut.rst, + period_ns=6.4 + ) + + dut.enable.setimmediatevalue(0) + dut.input_start.setimmediatevalue(0) + dut.input_start_valid.setimmediatevalue(0) + dut.input_period.setimmediatevalue(0) + dut.input_period_valid.setimmediatevalue(0) + dut.input_width.setimmediatevalue(0) + dut.input_width_valid.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) + + +@cocotb.test() +async def run_test(dut): + + tb = TB(dut) + + await tb.reset() + + dut.enable <= 1 + + await RisingEdge(dut.clk) + + dut.input_start <= 100 << 16 + dut.input_start_valid <= 1 + dut.input_period <= 100 << 16 + dut.input_period_valid <= 1 + dut.input_width <= 50 << 16 + dut.input_width_valid <= 1 + + await RisingEdge(dut.clk) + + dut.input_start_valid <= 0 + dut.input_period_valid <= 0 + dut.input_width_valid <= 0 + + await Timer(10000, 'ns') + + await RisingEdge(dut.clk) + + dut.input_start <= 0 << 16 + dut.input_start_valid <= 1 + dut.input_period <= 100 << 16 + dut.input_period_valid <= 1 + dut.input_width <= 50 << 16 + dut.input_width_valid <= 1 + + await RisingEdge(dut.clk) + + dut.input_start_valid <= 0 + dut.input_period_valid <= 0 + dut.input_width_valid <= 0 + + await Timer(10000, 'ns') + + await RisingEdge(dut.clk) + await RisingEdge(dut.clk) + + +# 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_ptp_perout(request): + dut = "ptp_perout" + module = os.path.splitext(os.path.basename(__file__))[0] + toplevel = dut + + verilog_sources = [ + os.path.join(rtl_dir, f"{dut}.v"), + ] + + parameters = {} + + parameters['FNS_ENABLE'] = 1 + parameters['OUT_START_S'] = 0 + parameters['OUT_START_NS'] = 0 + parameters['OUT_START_FNS'] = 0x0000 + parameters['OUT_PERIOD_S'] = 1 + parameters['OUT_PERIOD_NS'] = 0 + parameters['OUT_PERIOD_FNS'] = 0x0000 + parameters['OUT_WIDTH_S'] = 0 + parameters['OUT_WIDTH_NS'] = 1000 + parameters['OUT_WIDTH_FNS'] = 0x0000 + + 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, + )