From b4fbb18589cf7d2a60554f19ebe3c9a6fe3f52f1 Mon Sep 17 00:00:00 2001 From: Tomasz Hemperek Date: Thu, 25 Feb 2021 19:34:48 +0100 Subject: [PATCH] Support of cocotb 1.5 --- azure-pipelines.yml | 2 +- cocotb_test/simulator.py | 37 ++++----- setup.py | 2 +- tests/dff.v | 49 +++--------- tests/dff.vhdl | 49 ++++-------- tests/dff_cocotb.py | 160 ++++----------------------------------- 6 files changed, 62 insertions(+), 237 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index c9f2f72..6dcfab3 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -95,7 +95,7 @@ jobs: displayName: "Install pytest-parallel" - script: | - conda install --yes pip && git clone -b v1.4.0 https://github.com/cocotb/cocotb.git && cd cocotb && cd .. && pip install -e cocotb + conda install --yes pip && git clone -b v1.5.0 https://github.com/cocotb/cocotb.git && cd cocotb && cd .. && pip install -e cocotb displayName: "Install cocotb from source" - script: | diff --git a/cocotb_test/simulator.py b/cocotb_test/simulator.py index fbc5126..97e3717 100644 --- a/cocotb_test/simulator.py +++ b/cocotb_test/simulator.py @@ -10,6 +10,8 @@ import threading import signal import warnings +import cocotb._vendor.find_libpython as find_libpython +import cocotb.config from distutils.spawn import find_executable from distutils.sysconfig import get_config_var @@ -176,6 +178,8 @@ def set_env(self): for e in os.environ: self.env[e] = os.environ[e] + self.env["LIBPYTHON_LOC"] = find_libpython.find_libpython() + self.env["PATH"] += os.pathsep + self.lib_dir self.env["PYTHONPATH"] = os.pathsep.join(sys.path) @@ -349,7 +353,7 @@ def compile_command(self): def run_command(self): return ( - ["vvp", "-M", self.lib_dir, "-m", "libcocotbvpi_icarus"] + ["vvp", "-M", self.lib_dir, "-m", cocotb.config.lib_name("vpi", "icarus")] + self.simulation_args + [self.sim_file] + self.plus_args @@ -451,26 +455,26 @@ def build_command(self): RTL_LIBRARY=as_tcl_value(self.rtl_library), TOPLEVEL=as_tcl_value(self.toplevel), EXT_NAME=as_tcl_value( - "cocotb_init {}".format(os.path.join(self.lib_dir, "libcocotbfli_modelsim." + self.lib_ext)) + "cocotb_init {}".format(cocotb.config.lib_name_path("fli", "questa")) ), EXTRA_ARGS=" ".join(as_tcl_value(v) for v in (self.simulation_args + self.get_parameter_commands(self.parameters))), ) if self.verilog_sources: - self.env["GPI_EXTRA"] = "cocotbvpi_modelsim:cocotbvpi_entry_point" + self.env["GPI_EXTRA"] = cocotb.config.lib_name_path("vpi", "questa")+":cocotbvpi_entry_point" else: do_script = "vsim -onfinish {ONFINISH} -pli {EXT_NAME} {EXTRA_ARGS} {RTL_LIBRARY}.{TOPLEVEL} {PLUS_ARGS};".format( ONFINISH="stop" if self.gui else "exit", RTL_LIBRARY=as_tcl_value(self.rtl_library), TOPLEVEL=as_tcl_value(self.toplevel), - EXT_NAME=as_tcl_value(os.path.join(self.lib_dir, "libcocotbvpi_modelsim." + self.lib_ext)), + EXT_NAME=as_tcl_value(cocotb.config.lib_name_path("vpi", "questa")), EXTRA_ARGS=" ".join(as_tcl_value(v) for v in (self.simulation_args + self.get_parameter_commands(self.parameters))), PLUS_ARGS=" ".join(as_tcl_value(v) for v in self.plus_args), ) if self.vhdl_sources: - self.env["GPI_EXTRA"] = "cocotbfli_modelsim:cocotbfli_entry_point" + self.env["GPI_EXTRA"] = cocotb.config.lib_name_path("fli", "questa")+":cocotbfli_entry_point" if self.waves: do_script += "log -recursive /*;" @@ -487,7 +491,7 @@ class Ius(Simulator): def __init__(self, *argv, **kwargs): super(Ius, self).__init__(*argv, **kwargs) - self.env["GPI_EXTRA"] = "cocotbvhpi_ius:cocotbvhpi_entry_point" + self.env["GPI_EXTRA"] = cocotb.config.lib_name_path("vhpi", "ius")+":cocotbvhpi_entry_point" def get_include_commands(self, includes): include_cmd = [] @@ -533,7 +537,7 @@ def build_command(self): "-define", "COCOTB_SIM=1", "-loadvpi", - os.path.join(self.lib_dir, "libcocotbvpi_ius." + self.lib_ext) + ":vlog_startup_routines_bootstrap", + cocotb.config.lib_name_path("vpi", "ius") + ":vlog_startup_routines_bootstrap", "-plinowarn", "-access", "+rwc", @@ -563,7 +567,7 @@ class Xcelium(Simulator): def __init__(self, *argv, **kwargs): super(Xcelium, self).__init__(*argv, **kwargs) - self.env["GPI_EXTRA"] = "cocotbvhpi_ius:cocotbvhpi_entry_point" + self.env["GPI_EXTRA"] = cocotb.config.lib_name_path("vhpi", "ius") + ":cocotbvhpi_entry_point" def get_include_commands(self, includes): include_cmd = [] @@ -604,12 +608,12 @@ def build_command(self): [ "xrun", "-64", - "-elaborate", "-v93", + "-elaborate" "-define", "COCOTB_SIM=1", "-loadvpi", - os.path.join(self.lib_dir, "libcocotbvpi_ius." + self.lib_ext) + ":vlog_startup_routines_bootstrap", + cocotb.config.lib_name_path("vpi", "ius") + ":vlog_startup_routines_bootstrap", "-plinowarn", "-access", "+rwc", @@ -678,7 +682,7 @@ def build_command(self): "-sverilog", "+define+COCOTB_SIM=1", "-load", - os.path.join(self.lib_dir, "libcocotbvpi_vcs." + self.lib_ext), + cocotb.config.lib_name_path("vpi", "vcs"), ] + self.get_define_commands(self.defines) + self.get_include_commands(self.includes) @@ -737,7 +741,7 @@ def build_command(self): "ghdl", "-r", self.toplevel, - "--vpi=" + os.path.join(self.lib_dir, "libcocotbvpi_ghdl." + self.lib_ext), + "--vpi=" + cocotb.config.lib_name_path("vpi", "ghdl"), ] + self.simulation_args + self.get_parameter_commands(self.parameters) if not self.compile_only: @@ -803,21 +807,21 @@ def build_command(self): do_script += "asim +access +w -interceptcoutput -O2 -loadvhpi {EXT_NAME} {EXTRA_ARGS} {RTL_LIBRARY}.{TOPLEVEL} \n".format( RTL_LIBRARY=as_tcl_value(self.rtl_library), TOPLEVEL=as_tcl_value(self.toplevel), - EXT_NAME=as_tcl_value(os.path.join(self.lib_dir, "libcocotbvhpi_aldec")), + EXT_NAME=as_tcl_value(cocotb.config.lib_name_path("vhpi", "riviera")), EXTRA_ARGS=" ".join(as_tcl_value(v) for v in (self.simulation_args + self.get_parameter_commands(self.parameters))), ) if self.verilog_sources: - self.env["GPI_EXTRA"] = "cocotbvpi_aldec:cocotbvpi_entry_point" + self.env["GPI_EXTRA"] = cocotb.config.lib_name_path("vpi", "riviera") + "cocotbvpi_entry_point" else: do_script += "asim +access +w -interceptcoutput -O2 -pli {EXT_NAME} {EXTRA_ARGS} {RTL_LIBRARY}.{TOPLEVEL} {PLUS_ARGS} \n".format( RTL_LIBRARY=as_tcl_value(self.rtl_library), TOPLEVEL=as_tcl_value(self.toplevel), - EXT_NAME=as_tcl_value(os.path.join(self.lib_dir, "libcocotbvpi_aldec")), + EXT_NAME=as_tcl_value(cocotb.config.lib_name_path("vpi", "riviera")), EXTRA_ARGS=" ".join(as_tcl_value(v) for v in (self.simulation_args + self.get_parameter_commands(self.parameters))), PLUS_ARGS=" ".join(as_tcl_value(v) for v in self.plus_args), ) if self.vhdl_sources: - self.env["GPI_EXTRA"] = "cocotbvhpi_aldec:cocotbvhpi_entry_point" + self.env["GPI_EXTRA"] = cocotb.config.lib_name_path("vhpi", "riviera") + ":cocotbvhpi_entry_point" if self.waves: do_script += "log -recursive /*;" @@ -827,7 +831,6 @@ def build_command(self): do_file = tempfile.NamedTemporaryFile(delete=False) do_file.write(do_script.encode()) do_file.close() - # print(do_script) return [["vsimsa"] + ["-do"] + ["do"] + [do_file.name]] diff --git a/setup.py b/setup.py index d208a59..b3c0b57 100644 --- a/setup.py +++ b/setup.py @@ -21,7 +21,7 @@ def read_file(fname): author="Tomasz Hemperek", author_email="hemperek@uni-bonn.de", packages=find_packages(), - install_requires=["cocotb>=1.4", "pytest"], + install_requires=["cocotb>=1.5", "pytest"], entry_points={ "console_scripts": [ "cocotb=cocotb_test.cli:config", diff --git a/tests/dff.v b/tests/dff.v index 34aa97e..fa53685 100644 --- a/tests/dff.v +++ b/tests/dff.v @@ -1,40 +1,15 @@ -// ============================================================================= -// Authors: Martin Zabel -// -// Module: A simple D-FF -// -// Description: -// ------------------------------------ -// A simple D-FF with an initial state of '0'. -// -// License: -// ============================================================================= -// Copyright 2016 Technische Universitaet Dresden - Germany -// Chair for VLSI-Design, Diagnostics and Architecture -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// ============================================================================= +// This file is public domain, it can be freely copied without restrictions. +// SPDX-License-Identifier: CC0-1.0 + `timescale 1us/1us -module dff_test (c,d,q); - input wire c, d; - output reg q = 1'b0; +module dff_test ( + input logic clk, d, + output logic q +); + +always @(posedge clk) begin + q <= d; +end - always @(posedge c) - begin - // It is also possible to add an delay of less than one clock period - // here. - q <= d; - end - -endmodule // dff +endmodule diff --git a/tests/dff.vhdl b/tests/dff.vhdl index 8868d7c..3d7feaa 100644 --- a/tests/dff.vhdl +++ b/tests/dff.vhdl @@ -1,42 +1,21 @@ --- ============================================================================= --- Authors: Martin Zabel --- --- Module: A simple D-FF --- --- Description: --- ------------------------------------ --- A simple D-FF with an initial state of '0'. --- --- License: --- ============================================================================= --- Copyright 2016 Technische Universitaet Dresden - Germany --- Chair for VLSI-Design, Diagnostics and Architecture --- --- Licensed under the Apache License, Version 2.0 (the "License"); --- you may not use this file except in compliance with the License. --- You may obtain a copy of the License at --- --- http://www.apache.org/licenses/LICENSE-2.0 --- --- Unless required by applicable law or agreed to in writing, software --- distributed under the License is distributed on an "AS IS" BASIS, --- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. --- See the License for the specific language governing permissions and --- limitations under the License. --- ============================================================================= +-- This file is public domain, it can be freely copied without restrictions. +-- SPDX-License-Identifier: CC0-1.0 library ieee; use ieee.std_logic_1164.all; entity dff_test_vhdl is - port ( - c : in std_logic; - d : in std_logic; - q : out std_logic := '0'); -end entity dff_test_vhdl; +port( + clk: in std_logic; + d: in std_logic; + q: out std_logic); +end dff_test_vhdl; -architecture rtl of dff_test_vhdl is +architecture behavioral of dff_test_vhdl is begin - -- It is also possible to add an delay of less than one clock period here. - q <= d when rising_edge(c); -end architecture rtl; + process (clk) begin + if rising_edge(clk) then + q <= d; + end if; + end process; +end behavioral; diff --git a/tests/dff_cocotb.py b/tests/dff_cocotb.py index cc058a2..06747eb 100644 --- a/tests/dff_cocotb.py +++ b/tests/dff_cocotb.py @@ -1,154 +1,22 @@ -# ============================================================================== -# Authors: Martin Zabel -# -# Cocotb Testbench: For D-FF -# -# Description: -# ------------------------------------ -# Automated testbench for simple D-FF. -# -# License: -# ============================================================================== -# Copyright 2016 Technische Universitaet Dresden - Germany -# Chair for VLSI-Design, Diagnostics and Architecture -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# ============================================================================== +# This file is public domain, it can be freely copied without restrictions. +# SPDX-License-Identifier: CC0-1.0 import random - import cocotb from cocotb.clock import Clock -from cocotb.decorators import coroutine -from cocotb.triggers import Timer, RisingEdge, ReadOnly -from cocotb.monitors import Monitor -from cocotb.drivers import BitDriver -from cocotb.binary import BinaryValue -from cocotb.regression import TestFactory -from cocotb.scoreboard import Scoreboard -from cocotb.result import TestFailure, TestSuccess - -# ============================================================================== -class BitMonitor(Monitor): - """Observes a single-bit input or output of DUT.""" - - def __init__(self, name, signal, clock, callback=None, event=None): - self.name = name - self.signal = signal - self.clock = clock - Monitor.__init__(self, callback, event) - - @coroutine - def _monitor_recv(self): - clkedge = RisingEdge(self.clock) - - while True: - # Capture signal at rising edge of clock - yield clkedge - vec = self.signal.value - self._recv(vec) - - -# ============================================================================== -def input_gen(): - """Generator for input data applied by BitDriver""" - while True: - yield random.randint(1, 5), random.randint(1, 5) - - -# ============================================================================== -class DFF_TB(object): - def __init__(self, dut, init_val): - """ - Setup testbench. - - init_val signifies the BinaryValue which must be captured by the - output monitor with the first rising clock edge. This is actually the initial - state of the flip-flop. - """ - # Some internal state - self.dut = dut - self.stopped = False - - # Create input driver and output monitor - self.input_drv = BitDriver(dut.d, dut.c, input_gen()) - self.output_mon = BitMonitor("output", dut.q, dut.c) - - # Create a scoreboard on the outputs - self.expected_output = [init_val] - self.scoreboard = Scoreboard(dut) - self.scoreboard.add_interface(self.output_mon, self.expected_output) - - # Reconstruct the input transactions from the pins - # and send them to our 'model' - self.input_mon = BitMonitor("input", dut.d, dut.c, callback=self.model) - - def model(self, transaction): - """Model the DUT based on the input transaction.""" - # Do not append an output transaction for the last clock cycle of the - # simulation, that is, after stop() has been called. - if not self.stopped: - self.expected_output.append(transaction) - - def start(self): - """Start generation of input data.""" - self.input_drv.start() - - def stop(self): - """Stop generation of input data. - Also stop generation of expected output transactions. - One more clock cycle must be executed afterwards so that the output of - D-FF can be checked. - """ - self.input_drv.stop() - self.stopped = True - - -# ============================================================================== -@cocotb.coroutine -def clock_gen(signal): #TODO: to be changed after : https://github.com/cocotb/cocotb/issues/979 - """Generate the clock signal.""" - while True: - signal <= 0 - yield Timer(5, units="us") - signal <= 1 - yield Timer(5, units="us") - - -# ============================================================================== # ============================================================================== -@cocotb.coroutine -def run_test(dut): - """Setup testbench and run a test.""" - cocotb.fork(clock_gen(dut.c)) - tb = DFF_TB(dut, BinaryValue(0, 1)) - - clkedge = RisingEdge(dut.c) - - # Apply random input data by input_gen via BitDriver for 100 clock cycle. - tb.start() - for i in range(10000): - yield clkedge +from cocotb.triggers import FallingEdge - # Stop generation of input data. One more clock cycle is needed to capture - # the resulting output of the DUT. - tb.stop() - yield clkedge - # Print result of scoreboard. - raise tb.scoreboard.result +@cocotb.test() +async def test_dff_simple(dut): + """ Test that d propagates to q """ + clock = Clock(dut.clk, 10, units="us") # Create a 10us period clock on port clk + cocotb.fork(clock.start()) # Start the clock -# ============================================================================== -# Register the test. -factory = TestFactory(run_test) -factory.generate_tests() + await FallingEdge(dut.clk) # Synchronize with the clock + for i in range(10): + val = random.randint(0, 1) + dut.d <= val # Assign the random value val to the input port d + await FallingEdge(dut.clk) + assert dut.q.value == val, "output q was incorrect on the {}th cycle".format(i)